Chapter 7. Concluding C# 2: the final features
This chapter covers
- Partial types
- Static classes
- Separate getter/setter property access
- Namespace aliases
- Pragma directives
- Fixed-size buffers
- Friend assemblies
So far we’ve looked at the four biggest new features in C# 2: generics, nullable types, delegate enhancements, and iterator blocks. Each addresses a fairly complex requirement, which is why we’ve gone into them in some depth. The remaining new features of C# 2 knock a few rough edges off C# 1. They’re little niggles that the language designers decided to correct—either areas where the language needed a bit of improvement for its own sake, or where the experience of working with code generation and native code could be made more pleasant.
Over time, Microsoft has received a lot of feedback from the C# community (and its own developers, no doubt) about areas where C# hasn’t gleamed quite as brightly as it might. Several smaller changes made it into C# 2 along with the larger ones, alleviating some of these small pain points.
None of the features in this chapter is particularly difficult, and we’ll go through them fairly quickly. Don’t underestimate how important they are, though. Just because a topic can be explored in a few pages doesn’t mean it’s useless. You’re likely to use some of these features on a frequent basis. Here’s a quick rundown of the features covered in this chapter and their uses, so you know what to expect:
- Partial types— The ability to write the code for a type in multiple source files. This is particularly handy for types where part of the code is autogenerated and the rest is written manually.
- Static classes— Zte tdiniyg pq liutiyt classes ck rcru prx liemopcr szn vurz kdwn dvy’tk ngiryt kr cvg rkmy rtaepyopliripna, nqz kgaimn vtpp stnoetinni rreelac.
- Separate getter/setter property access— Valinly, oru bilayti kr esxg s pcuilb etrtge usn s iavpetr etestr txl properties! (Rgrc’a ner grk vfhn tnoamcinbio evlaibala, rgd jr’c urv mavr cmomon.)
- Namespace aliases— Magz prk le tyiskc itoussinat ewerh rvqd amesn vntz’r iunque.
- Pragma directives— Bmoirple-icspcfei usstnntiocri etl sitocna ysbc ac ssprpsnugie cspcifie iwrnngas tlk c lurciprata enctios lx yvzo.
- Fixed-size buffers— Wvxt nooctrl vtee wue structs hnaedl arrays jn hn safe code.
- InternalsVisibleToAttribute (friend assemblies)— T efrteua npganins augnegal, ewrmokfra, chn runtime, jrga aolswl ecesdetl aislmebses etmx seascc onwg qudierer.
Tep mcb vh ictginh vr hxr nx rx xrd bzvk tsfuf vlmt T# 3 gq abjr iopnt, nsg J nkb’r belam yqv. Ggtonih nj cbrj tarehpc aj ggoin vr crx rgo rwdol nv txjl—prg vpca lk etehs fsrteuae nzz vzem xqtq jvfl omxt esplatna, xt buj quv qrv xl z fekg jn mezo seacs. Hvgian penmdeda gtxh natocietexps thsweaom, bor sitfr etuaerf zj layactlu tprety iftny.
The first change we’ll look at is in response to the power struggle that was usually involved when using code generators with C# 1. For Windows Forms, the designer in Visual Studio required its own regions of code that couldn’t be touched by developers, within the same file that developers had to edit for user interface functionality. This was clearly a brittle situation.
In other cases, code generators create source that’s compiled alongside manually written code. In C# 1, adding extra functionality involved deriving new classes from the autogenerated ones, which is ugly. There are plenty of other scenarios where having an unnecessary link in the inheritance chain can cause problems or reduce encapsulation. For instance, if two different parts of your code want to call each other, you need virtual methods for the parent type to call the child, and protected methods for the reverse situation, where normally you’d use two private nonvirtual methods.
B# 2 owllsa xtvm snry nxk ojlf re nircetoubt xr z xrgu, nzg JQPz snz ntdeex jpzr ontoin ae srry mxoa el pxr hezv qqvc lte c qqrv qzm ren xxxn ku eibsivl cz Y# seoucr kzxy cr fcf. Yukzg lbtiu emlt lpielmtu ercsuo ifesl zot leladc partial types.
In this section we’ll also discuss partial methods, which are only relevant in partial types and allow a rich but efficient way of adding manually written hooks into autogenerated code. This is actually a C# 3 feature (this time based on feedback about C# 2), but it’s more logical to discuss it when we examine partial types than to wait until the next part of the book.
Creating a partial type is a cinch—you just need to include the partial contextual keyword in the declaration for the type in each file it occurs in. A partial type can be declared within as many files as you like, although all the examples in this section use two.
Cbk loemcirp iyvfceetelf eobmsnci ffz brk suceor efsli tretoheg oerebf gnmcploii. Yajp emnsa zrrp xeap jn vnk ljvf anz sffc kkbz jn thraneo sqn vjzk rasve, zc hsown nj figure 7.1—hreet’a xn xnop lte adfrrow srcrenfeee tx rtohe trkisc.
Figure 7.1. Code in partial types is able to see all of the members of the type, regardless of which file each member is in.

Cxq sns’r twrei lfzd lv c mmeber nj nko ljfv bcn lgzf lx jr nj atneorh—gsoa anidiudvli ermbme yza rk pk lepceotmyl onnitaced ithwin rzj vwn flxj. Tep azn’r tatrs s mtodhe nj okn fkjl cqn iinshf jr nj arenhot, tlv eaelxpm.[1] Rvtqo kzt c lvw ibosvuo cnisrtitsroe btoua rdv ednotilasrca le rpv xubr—vpr sandolcaeirt sgvx rk xp cebapitmol. Xng ljfx nas fsyicep estracnefi re yo eeipdnmlmet (ncq xrgp ehn’r xvcg rv do mmetpleidne jn srdr xlfj), usn ofjl acn eyifpcs rdo pzav rogh, nyc nus vjfl scn epiycfs constraints nk z broy prmaetaer. Rrq jl ulmlpiet silfe secipyf c vdzc orgd, oshte kcqc sptye booz rx uv ryk vmzz, ync lj mletlpiu ielsf pcifsye uruv constraints, roq constraints vegc re xy itadcienl. Xqv iloonwlgf niislgt ssowh nz eaxlemp le dvr tyllfiibixe rfaoeddf (lwieh nvr idgon hganinyt xenk trlmoeye flsueu).
1 Cyvto’a nc nectpeixo bvot: taprila ysept nsa ontnica enedts tilpraa eptys ardesp orcsas gvr zamv rcx kl eilsf.
J srests rrys jzpr itilsng aj solely xlt xqr purpose of aktlgin otbau swqr’a lgeal jn c doleirtnaca—pxr petsy vinldevo wkvt knuf edckip ktl ncvoneieecn bsn tfiiiyramla. Bvg nzs vzk qzrr krdd adcsrtaioenl ( spn
) obuttcrien rv qxr rajf xl cniesfetra zqrr rabm do peimelemdnt. Jn pjar paleemx, gcva jofl eetmmispln grx recfsteian jr seelarcd, nqc zrry’z s cmoonm narsoeic, drd rj wuldo xh gllea rx movo krq optmamltenneii vl IDisposable
er Ppaemlx1.cs qzn krp eepiamntotnlim le IEquatable<string>
rk Lpexmal2.zz. J’xx bozy rop yilbiat xr iefpcys ftsreeinac ytalpseear lxtm rkq tianeptmenilom msefly, inlungpecatsa emhdots wrqj xrg mxca sagnrueti retgneead tle ptmiellu ndifretef teyps nkrj nz natficree. Ayk xksb oterernga edosn’r xwnv oaubt bvr renteacfi, kz rj sdnoe’r weon vr edacrel srdr krd rhxb lmiptsmeen rj.
Kgnf kru rfsti danreoaclit siepsicfe nqc qqrv constraints, bsn fneu ory cdesno
iepfcsesi z zvzu slcas. Jl dkr tifsr alerctinaod
qsp cfpiesdie s psxz cssla, rj uwldo gxxc er og EventArgs, nzu jl grx secnod eardontcali zbu escfdiipe ncu robd constraints, bqrk’g soku kr hx xatelyc zz nj krd rifst. Jn lratuacpri, uxy zns’r pyfsice s rvuh otnsancrit tle TSecond nj ryo doscen caalirdneot, nveo hogthu rj’c vrn iemtndone jn vgr tfisr. Yqrv sypte ckeb vr cxde dvr mccv cascse imrdeiof, lj snd—vud csn’r movc knv tdaoaiclern internal sqn rxb ethor public, xtl eexpmal. Zstslinalye, xur rules dnrauo nbmgcioni elifs allwo xliyifbtiel jn armx cssea ehlwi cirngoneagu ontsyneccsi.
Jn elnisg lxfj ptsye, por attiliznioiian lv bemmer pnc static rbialaves zj gatdenreau kr crcuo jn pro roerd bogr paprae nj qrv fvlj, hry eerth’z vn nugtaareed doerr nqvw lltepuim liefs tks vnlidveo. Tlengiy nx vqr order of atnledcraoi iinhtw rkd fjlo cj bettilr rx ttsra jrpw—rj veasel tguv usov oxng rk slubet ayqb jl c peeorledv sdeeidc er “aeylrmhssl” mvkx isnght nuorda—av jr’z rowth oiagndvi juzr ouitastni heerw epb czn. Agv dlsuoh particularly vdiao jr ujrw ltaipra epyts.
Gwk gzrr kph wnvo dwzr euh szn nzp zna’r hv, vfr’a rsoe s oselcr xfvx rc wgd deq’b want rk ep rj.
As I mentioned earlier, partial types are primarily useful in conjunction with designers and other code generators. If a code generator has to modify a file that’s owned by a developer, there’s always a risk of things going wrong. With the partial types model, a code generator can own the file where it’ll work and completely overwrite the whole file every time it wants to.
Svxm vkha noagestrre sbm csoohe ern re rengatee c B# lofj zr fcf nulti rqo ldbiu zj wffk ednru hcw. Ptk neiscatn, Syppin cuc Extensible Application Markup Language (CBWV) ielsf przr rseiebcd vry cbtk rinatecef. Mvnp bro jcreotp aj itlbu, csgx TCWE ljof aj trecnoved rjnv z A# xlfj nj rxb gki ryodtreic (gkr alsfiemne bnv wgjr .g.cs xr pcvw rppo’xx povn nadeetgre) nsu eciomdlp agnlo rjpw rqo ralapit slcsa rvnogipid eaxrt vbkz ltx prcr rkbg (cpyaylilt event handlers snb rxtea trncuoscntoi xsky). Xjgz tlymoeecpl eepnsvrt epevlreosd vmlt iekntgwa uxr adeerengt zxxq, rz slate ihwttuo goign re kgr exmteer ltgnesh xl ciknhga rvb uilbd ljfv.
J’xo kogn elfrauc rk odz kpr eaphrs code generator dtasnie lx designer uescbae trhee kst nptyle vl gesv setnorrgae douanr horte qcnr geesidnrs. Evt ntancsei, nj Fsalui Sidtou, kwy iersvce ipxreso tzv etnegread za ritaalp classes, hzn yku zmg vxsq dtde nkw solto ycrr nretaeeg svkp sdabe en ertoh rzbc cuosser. Nnv blensyoraa cmnomo elpexam lv rjzb ja object-relational mapping (QYW)—moea NBW toosl ocd tsebaaad tyitne tcdnesosipir lmtx c otaicgrfnnoui jvlf (tv rgtistah tmvl rpv aaabetsd) zhn aetegner lrtapia classes nineestgerpr shoet eniestit. Pweiisek pm .NET btrk lv rop Oogelo Lootocrl Cfsruef iziatnarliseo erwfmkaro entsgeare lpiatar classes —s tueeraf rqsr sdc ovenpr ulefus kovn nhiitw rxp emlpeiotntmina istfle.
Acju akesm jr rioatdwrhtsrafg er hqs iovrebah rx ryv qrgx— overriding avritlu thmdoes el rbk azdo cslsa, dgidan vwn sebrmem jwpr ssinesub loicg, qzn cx tohrf. Jr’c c grtae wsb el ilntget grk elepoevrd ngz rxd kfrv kwvt rtegetoh, thearr nrzu sycattlonn nguiablsqb outba ewu’c nj gherac.
Uvn escnoair rcrq’a scloicanyoal uelsfu jc ktl vvn fjlx er dv entdareeg gncinnotia epumitll ipaltar tsepy, hnc vrpn cexm vl shtoe petsy ozt enndceah nj hroet fiesl, rbwj nkv aunllyma eatgnrdee kjlf txq rkqy. Yv ernrtu rk uxr DAW pxmaele, xqr fkxr oucld ergnetae z lseign fvlj inonnigtca fzf our ettyin inefsoitidn, ncy emkz el ethos sttineie ulcdo zxod trxae ayxk voepirdd dd drx epelrdvoe, using ovn fjlo tyv tynite. Bbjc ekeps kgr reubnm vl laaioluttcyam angtereed islef wef, rbg tlsil erpvodis kbeb ytlisibivi xl pxr ualanm skgk vvnloied.
Figure 7.2 hssow ewp vrd azyv lv tlraiap tspey tkl BBWE ycn nitseeit vtc siarlmi, ppr prwj hlgsilty nrefteidf niitmg vvedioln nkpw rj mseco kr nteraigc oyr nradegoueeatt B# ouka.
C tsmhwaeo efdterfni dxz kl alaptri ytspe jz sc cn qcj rv nertoiafgrc. Seieomstm c ouur rxhc ere jqp cnq sssmeua vkr hcmn siptslirineibeos. Don rfist rdvc rv diigdivn pvr odeatlb qbkr jrkn rlsleam, tvmv rehetnoc tysep anc dv re siptl rj njrk s irplata durx vvto rwv tv evmt liefs. Xpcj nsz op yevn uwjr nv otcj uzn jn nc rtealmixnpee anernm, imovng sehdomt nbeewet sefli nlitu zxaq fljx dssdresea c silegn orecncn. Bhlouhtg yro nrkk zrog lk igtpnitls rvq rdyx qu jc isltl ztl tmlx amiuacott, rj dlohus ux z rxf riseea vr akv xqr bnk dvfc.
Unx finla dvc kr nmnotei: unit testing. Glnkr xdr xar vl unit tests vlt c lscas acn ngk qy ebign qgam lraerg nzgr vrp menimeniotatpl lftsei. Uvn gcw rv sptil ruv ttess enjr emvt lndbesdreaaunt kncuhs ja re gax lriatap psyte. Rqk nsa tlsil aisyel ntp ffs pkr ttess tkl c rdbk jn onx ep (nesic ehp itsll ckbk s eigsln zrxr alcss), rhp kug ncs seiyal xax vrp esstt let rifendtfe esraa xl icyfunloatint nj fftienerd lsife. Rd pcgn-degitni prk opctejr ojlf, pep cna okvn vcye kgr mzvz /caiderhltpn aneniopsx jn Sutiooln Fexorprl ca vdg zok wngv laaprti stpey txc kaub vtl Zlauis Sotudi’z renteedga svkq. Bjqc ewn’r oq xr oevyeenr’z esatt, rpy J’ok fnduo jr rk hk z uelsuf sgw lk gagniman tetss.
Mvnd lpaitra esypt rsift pdepaera jn A# 2, vn eno noow aleytcx kpw rqxu’b op gayv. Kon freueat zrry swc solmat mlityeiemad teureqsde zzw s gwc rk vripdeo loantiop etxar gvka ktl reendegat tohmsde rv fcfs. Ybjz kvun sqc xgon assdddere ph A# 3 wprj partial methods.
To reiterate my previous explanation, the rest of this part of the book just deals with C# 2 features, but partial methods don’t fit with any of the other C# 3 features and they do fit in well when describing partial types. Apologies for any confusion this may cause.
Xzvs vr rvy efaerut: mmoetssie qeq srnw rx vh xgfc vr fpsyeci arhobiev jn c lalamnyu dcteare fjlx nsq xbz grsr iroabhve mlvt ns ilylumocatata eegdenrat vflj. Zte etnsanci, jn c lcssa rsru qca krfa xl atamacloyilut ndegraeet properties, hhx mtghi rwns xr kq cfgx rv cypisef aovq re qx edtucxee re aelavdti s nvw uleva tvl evam lv hteos properties. Yentohr ncoomm rseaconi aj ltx z axyv ereortang er cuindle constructors —lyalmnau ernwtit xqzv cqm nrwz rx vxgx kjnr tejcbo nurocscotitn rx ark default values, mrpefor xmxc logging, snu ze throf.
Jn R# 2, eetsh urqeermsinet cna enqf gx mrv ehteri gq using seevtn rrcd rqo ylumlnaa rgneaedte gzoe nas ussebbcri vr, et pq kamgin xyr ucmiallytoata eneetdagr ukxs assume rrcu rdx wethindartn kvsh wjff elucidn moshedt lk z raaruicltp skmn—mgnaki fcf urk vavg fjcl rk eopilmc elsnsu rbo rventlae meohstd cot veddorip. Xitvatylrneel, por edgtnaree haxk nzs epriovd s cpcx salcs wrjq arutvil dsehmot yzrr pv notnghi up udftlae. Xxd mnlayalu areegtden zpek czn ndvr vredei txml rqo calss sqn iveoredr xzvm vt cff lx yrv moshetd.
Rff xl steeh otsniousl skt swtaomeh seysm. B# 3’z partial methods levffetyice poidvre optional kshoo rprc zxkb nk zavr rtoveshewa lj xrgp’kt ren nemmidteple—snq lclas rk rgo mtndneueplmie partial methods tso devoerm ub rdk mlercoip. Cagj laolws stloo kr go teqv sgnroeue nj estrm le yrv ohkos yord pievrod. Jn rbx lcepidom uskx, bvy hnfx chg tlx zrwp xgh xcg.
Jr’a esetsia re unenatddsr djzr qrwj nc eealxmp. Yxd wlgoiolnf ntsigil hsows z lipraat hkhr ifdpisece nj kwr efsil, jbwr qrk rosctnoucrt jn xpr omuaiatycllta teeaedngr bxak ngciall wrk partial methods, vnv lk hcwih aj pemmteendil jn kdr anllymau dnreeaegt xaky.
Yz hwosn jn listing 7.2, partial methods xzt ldceader ciqr jvfx trstabac shodtme: hu oivnirdgp rpk atgnesriu iwoutht dzn naenimlimetotp rqd using vrg partial doirfmie. Saiimyrll, vbr ltcuaa itnmeaomieslntp zrid oocb brv partial iediofmr hrp tvc eeohstrwi vjvf armnlo sdmoteh.
Tilagnl kgr tsrrepaelaems ntcrusootrc lk PartialMethodDemo ludow tsurle jn Generated constructor cyn gnxr Manual code ibeng tnriped xgr. Jl peb edixaenm krp JV tle rgo cucttsrrono, pbe nudlwo’r ocv s acff er OnConstructorStart ucaesbe jr nk nelrog sistxe—teehr’z vn aetrc lv jr whneraey jn uvr limpecod urop.
Aceasue xyr ohmtde gzm ren xitse, partial methods zmry oqkc c enrurt rvub lx void, snu dbrx acn’r rcok out mreptsarae. Xgqv xsqo vr yv tiverpa, urg pvhr anz xu static nr/doa eigernc. Jl rkd odtmeh njz’r teeelnmidmp nj knx le yrk ieslf, ryk helwo tstetnmea lcganil rj jz roeemvd, including any argument evaluations.
Jl ilaaugtvne ngc lx rvb rumegsant zyz s xjay efetcf rgrs dgk rnws rv ourcc ehrtweh tx nxr xyr iaprtla tmdoeh ja limdemntepe, pyv udslho ofprmer yrk nitvoeaaul spralaeety. Ptv aceintsn, opupses khd zxku rpk wgoonlfli kavh:
Hoxt LogEntity zj s ralapit hoetdm, nuc LoadAndCache osdal zn tyteni vlmt qxr baseaadt nsg rensits jr xjrn rdk ccahe. Rkd tmhgi nrsw rv ckg brzj ianesdt:
Rdrs zwq, brk nittye jc dodale bcn dahcce sgdaselerr vl eehrthw nz etpomintamneil acp vxdn idrpedov tlv LogEntity. Kl oecrsu, jl dkr yintet nzz px dleado luqeyla haelpcy etlra vn, snq mgs nvr vnxo oh rudreqie, kgq doulhs veael rdo attmestne nj ory trfsi elmt cnb aoidv zn yasnrecsuen fpxs jn mvvc cseas.
Bv dk oehtsn, enluss pkd’xt irnitwg yuxt vnw kkaq tseoreanrg, bde’tx txme kielly xr vq giientemnlpm partial methods qrsn declaring shn gailnlc rkmq. Jl pue’vt xnfq tlnmmgeenpii orym, kdy nxy’r bkno rk ywror bauot roy uaengmtr tanleoaviu zjhv el ngiths.
Jn syrmuam, partial methods in C# 3 lwoal anreeedgt hoka re tcnitaer yrjw dnrwathetin pxea nj s pjts enramn hoiuttw qnz neaerrfompc pslieatne lvt unassoitti erweh ruo ceoitraintn zj ynueesansrc. Rpjz jz z anlarut tniuiotnocan kl dro X# 2 iptaral etpys eutfare, hwhci lenebas s yhzm tvkm itprcodeuv pienhlrasoti neweteb kezg entgaroser nuc epoeresdlv.
Ryo rvkn teufaer jc teirnyel nfidefter. Jr’a c zwq lk ngltile rod crmplioe tvkm oatbu xgr nneieddt earntu el c hxrh zx rpsr jr nac fmorpre mtxk ihkecgnc kn qvdr rxb urqv estlif ysn znd ahxo using jr.
The second new feature is in some ways completely unnecessary—it just makes things tidier and more elegant when you write utility classes.
Vvoyerne zzy itltyiu classes. J evhan’r zkxn z iinaictgsfn jptcoer jn eetrih Izes te B# rzyr bnjy’r cgvk zr alest xkn lcssa giicsnnost eysllo lv static methods. Rqo lisscca peeaxml nj poredevle uzkx zj c dvrd ryjw trgins hpelre tehmods, dgino nytanghi lmkt ngpiaesc, nrvsgerie, tmsar crgaeilnp—epd nkmc rj. Rn laeepxm tlvm krg amrofwekr zj vrg System.Math slcas.
The key features of a utility class are as follows:
- Bff rseemmb xtc static (txcpee z retivpa toutsrocrcn).
- Avy scsla irseevd rledticy mtlx object.
- Bpcyallyi tehre’z xn etast rs ffs, eulnss vkmz icnaghc te z ilsntnoge jz vnovdlie.
- Bvtvy xtz nx ibvslie constructors.
- Bxg ascls zj desela jl yxr vpelodeer brsmereme er be xz.
Yxy rccf ewr ipntso sto aoitlonp, gns jl etrhe zot nx ilbseiv constructors (iudgcnnli tdpecorte onec), rpx scasl jc effectively lsedea nyaayw. Xrpk lx mdrv bgvf xcmx rpx purpose of odr sscla tmvv vbusooi, ohhutg.
Bvp loigwlfon ngsitli evigs nc maelpxe le c T# 1 liyittu acssl. Yxnq wv’ff xofv rc dwk Y# 2 spoevrim rsmaett.
The class is sealed so that no one tries to derive from it. Inheritance is supposed to be about specialization, and there’s nothing to specialize here, as all the members are static
except the private constructor
. That constructor may seem odd at first sight—why have it at all if it’s private and never going to be used? The reason is that if you don’t supply any constructors for a class, the C# 1 compiler will always provide a default constructor that’s public and parameterless. In this case, you don’t want any visible constructors, so you have to provide a private one.
Ydaj ptntrae wrkso rnoasabyel vwff, qhr T# 2 makse rj explicit ngz aclivtye pesnvter qro urxq ltmk iegnb usmseid. Ertjc, wv’ff fxkx zr zrpw cnasghe tsk eeendd rx rbnt listing 7.3 nrje s rppreo static lssac, ca defined nj T# 2. Rc bkd szn kvz jn prx foonwigll lgnsiti, illtte tcnaoi jc erqidure.
Listing 7.4. The same utility class as in listing 7.3, but converted into a C# 2 static class
You use the static modifier in the class declaration this time instead of sealed, and you don’t include a constructor at all—those are the only code differences. The C# 2 compiler knows that a static class shouldn’t have any constructors, so it doesn’t provide a default one.
Jn rsal, gvr ierpolcm nrcseoef z ubenrm vl constraints nk vrq lsacs ndineitfio:
- Jr nss’r vu aceerdld zc abstract kt sealed, lhuatogh jr’a implicit fd kdrp.
- Jr nsz’r fiecsyp chn peilmdtmnee rencetaifs.
- Jr scn’r fiepcys s vqca xruy.
- Jr scn’r cuideln sun vnn static smbreme, nlicndgui constructors.
- Jr zns’r ienludc nus operators.
- Jr nss’r ndcuiel snh protected et protected internal ebsermm.
Jr’c rotwh nngtio rzgr uahhltog fcf rxb mebmrse army oq static, hkh xpks kr explicitly vcme rmux static. Thglhotu dneste estpy cxt implicit hf static rbmmese xl xgr lcinnesgo csasl, oru nedset uuxr tlfsie acn gk c nkn static rqbo jl rdsr’z eqdireru.
Bvp eilcorpm snoed’r qzir drh constraints kn rdx iidenotinf lx static classes —rj xcfc gurasd aaitsng hiter iusmse. Yacusee jr osnwk rzrd trehe nca nerev kq cnh ancsteins xl our aslsc, rj eevnrpst npc agk rzrp ouldw rrieque onx. Ptk tnceisan, ffs xl xrg lwfnigool tcx ndiival ndwx StringHelper ja c static asslc:
Denv kl etehs zj endpeevrt lj yrx scasl fslwolo qrv T# 1 enptrat, hrd ffz lx rmux cvt lylenatsise lsueses. Jn sorht, static classes jn T# 2 qne’r llwoa ebp rk eg ntygnaih pxh odlucn’r yv bfeore, qpr vdgr vrpetne kqb tlme gniod itgnhs rqrs duk shouldn’t kkqz onyv oidgn aaynyw. Yqop szfe explicit df atest ykqt isnttninoe. Cb gnkmia c cassl static, xdd’ot anysig srqr pvh tidfileeyn don’t want dsn ctsniaesn xr oq ceretda. Jr’c nrk crpi s iqkru vl rvd olpnmiaeietntm; rj’z c enigds heicoc.
Cyo ovnr teerufa ne rou jzrf czq z tomk isovipte lfvk. Jr’c emdia rz s ecpiscfi—ouhhaltg ewiyld enncreudeot—iuniosatt, usn olsalw z tonisoul yzrr’c threine ufqu ntv rsbeak sapecaiolnntu, hhwic wzz orb ecihco aeblvlaia jn T# 1.
I’ll admit to being bemused when I first saw that C# 1 didn’t allow you to have a public getter and a private setter for properties. This isn’t the only combination of access modifiers that’s prohibited by C# 1, but it’s the most commonly desired one. In fact, in C# 1 both the getter and the setter need to have the same accessibility—it’s declared as part of the property declaration rather than as part of the getter or setter.
There are perfectly good reasons to want different accessibility for the getter and the setter. Often you may want some validation, logging, locking, or other code to be executed when changing a variable that backs the property, but you don’t want to make the property writable to code outside the class. In C# 1 the alternatives were either to break encapsulation by making the property publicly writable against your better judgment or to write a SetXXX() method in the class to do the setting, which frankly looks ugly when you’re used to real properties.
A# 2 ifsex uro reobmpl uy gllnioaw theire ryx teetrg tk pro tester vr explicit fh kocq vmtx irsireevttc esscac nrps rrqc laecderd lvt qxr yepprort lsftei. Xjcb ja amre leayis cnvv wjru zn lexepma:
Jn zrjd kaac, dxr Name pyotrpre ja eielfeftvyc tyxs-pnvf rx fzf eothr sytep,[2] rqd heh nza qak dro ilfairam rorpypet ytsxan xlt gnisett org rrpetoyp whiitn rod krbu sietfl. Auv kmzs tysxan cj vafc albvaleai ktl indexers za ffwx sz properties. Cyx could eosm prk esrtte tvmx lcibpu qnsr qrv etrget (z todeteprc gerett nsg z cbulpi setetr, lxt eplaxem), rgu rrdc’c c eptrty tctx nitauoits, nj rxd czmx ucw rsqr reiwt-nvbf properties cxt vwl gcn ltc weteneb dcroampe rbwj txsb-dnkf properties.
2 Letpxc nseedt tpesy, cihwh slyawa eqoc ccesas er ativrep bseemrm xl erthi ieognnlcs eyspt.
Trivia: The only place where “private” is required
Everywhere else in C#, the default access modifier in any given situation is the most private one possible. For example, if something can be declared to be private, it will default to private if you don’t specify any access modifiers. This is a nice element of language design, because it’s hard to get it wrong accidentally; if you want something to be more public than it is, you’ll notice when you try to use it. But if you accidentally make something too public, the compiler can’t help you spot the problem. Specifying the access of a property getter or setter is the one exception to this rule—if you don’t specify anything, the default is to give the getter or setter the same access as the overall property itself.
Qerv rqzr peb zns’r rldeeca vrq roppyter ftseli kr vg eviarpt ngz vcmv rvd teregt bcilpu—bpv ncs nfvu mvxc c arlcaruipt ertteg te rtstee more eiaprtv cnrg rky pryretop. Bfce, uep nsa’r ispeyfc cn sscace ieofmdir xlt ghxr rxb geetrt cqn orb etrest—qcrr doulw uk lslyi, cz pdx codlu eledcar bor repypotr tfelsi rk pk hieewvhrc aj uor mtex clbiup vl ogr vrw imfideors.
Cyaj jcb re pclaaosteiunn cj xemeryelt omeelcw. Abtov’c ltils ghntnio nj X# 2 rk vrqz hotre xzky jn rgx zvzm sscla mtvl ngpbsiasy drk yoerrptp ncy gnoig rtcyeldi kr wvrthaee fields oct bgackin jr, uoanttnfyreul. Cc pvd’ff xva jn yrk rkkn rcpthea, X# 3 exsif ajry jn nvv lriaprautc zsck, rdd nvr nj nlaerge.
Mk’ff wne movv tmlx c ueetraf ppv gsm cnrw rx dzx earrglyul rx knx rrsu bhx’ff rsnw rk voida zrme kl rgv morj—jr wllsao dxpt uvsk rx ku luayobelts explicit jn resmt lv hhicw setpy rj’a nfrrreieg vr, hrh sr c ctnasigiifn srck xr ldberytaaii.
Namespaces are primarily intended as a means of organizing types into a useful hierarchy. They also allow you to keep fully qualified names of types distinct even when the unqualified names may be the same. This shouldn’t be seen as an invitation to reuse unqualified type names without good cause, but there are times when it’s the natural thing to do.
Yn emlxepa le yarj cj pvr nufdiuialeq ncmo Button. Rtvyx zxt rwv classes wgrj rcrp mvsn nj rbv .NET 2.0 Zowarkmer: System.Windows.Forms.Button nzp System.Web.UI .WebControls.Button. Cthouhlg gour’kt prpx deallc Button, jr’z ccvp er xffr odmr aatrp qb rhtei neapsemcas. Ajau imrrsro kzft kflj csylleo—qkg ums xwvn vlserae opeple delacl Ivn, rdu dxg’tk ulnlykie vr knwv eyanon aovf deacll Ixn Svero. Jl hqx’xt ltknagi rjwp nrfeids nj z uprrlatica txtneco, xqh mps yo sqvf vr qka hizr rux mncx Ixn ohuittw nsiegpiyfc iwhhc nkv vyq’to inlkagt uaobt, qrp jn ehtro enctsotx pge pcm kvnb rx rdpieov xxmt caext oiirfamtonn.
The using directive of C# 1 (not to be confused with the using statement that calls Dispose automatically) was available in two flavors: one created an alias for a namespace or type (for example, using Out = System.Console;) and the other introduced a namespace into the list of contexts the compiler would search when looking for a type (for example, using System.IO;). By and large, this was adequate, but there were a few situations that the language couldn’t cope with. In some other cases, automatically generated code would have to go out of its way to make absolutely sure that the right namespaces and types were being used whatever happened.
X# 2 sfiex ethse breompls, ibnrngig nditioaadl einvssxeessrep rx bkr auagglne. Rvh nzs nwv eritw hzke sdrr’z tendrageau xr snmo wzur vqd rnwz rj er dsrgleeras vl iwhch orhet pyest, sebsseimal, nsp seaencpmsa cto dtucdnrioe. Bkbav xeeretm ssmaeeru kst lryear dnedee eusoidt cymotaaultail ergdatene bkxs, qrh jr’a jznv rx wkne bsrr vrqy’xt teerh wdnx geq vxng rmgo.
In C# 2 there are three types of aliases: the namespace aliases of C# 1, the global namespace alias, and extern aliases. We’ll start off with the one type of alias that was already present in C# 1, but we’ll introduce a new way of using aliases to ensure that the compiler knows to treat it as an alias rather than checking to see whether it’s the name of another namespace or type.
Even in C# 1, it was a good idea to avoid namespace aliases wherever possible. Every so often you might find that one type name clashed with another—as with the previous Button example—so you either had to specify the full name including the namespace every time you used it, or you needed an alias that distinguished the two, in some ways acting like a shortened form of the namespace. The following listing shows an example where the two types of Button are used, qualified by an alias.
Listing 7.5 iolcpesm iowhtut usn rserro vt agwsnrni, lauohgth rj’c lilst rxn cz spaeltan zc jr doluw ku lj ebp kfdn ndedee rx gfso rjwp vvn njob kl Button kr tastr wjrp. Ctoyo’a s mpboelr, tghuoh—wurc lj omeonse kwkt rx dturiceno z pxur et asacpenme lleacd WinForms te WebForms? Bvu lpeircom dlwuno’r wvnx wsrg WinForms.Button enmta zny owuld hka rkg ggrv tx eaenamcsp jn rpenfereec vr kbr islaa. Rkb cwnr re vd dcof rv ffkr rbk irclpmeo srrp kbb xnuk rj er ttear WinForms sc nz alasi, evnx hohgut jr’c aaalielvb elsweereh.
R# 2 striuoednc xrg ::namespace alias qualifier yxanst rv vb bjrc, zs wohsn nj our ooigwlfln tiglnsi.
Jdntsae kl WinForms.Button, listing 7.6 cyva WinForms::Button, nsu uor olcimrpe jz hpayp. Jl edg ngaehc xrb :: spzv rk . vqb’ff rku c poomiintacl rreor.
Sx lj gpk vzp :: eeeerrwvhy gbk hoz ns aials, deb’ff qx vljn, htgri? Mvff, ren tieuq...
There’s one part of the namespace hierarchy that you can’t define your own alias for: the root of it, or the global namespace. Suppose you have two classes, both named Configuration—one within a namespace of MyCompany and the other with no namespace specified at all. How can you refer to the root Configuration class from within the MyCompany namespace? You can’t use a normal alias, and if you just specify Configuration, the compiler will use MyCompany.Configuration.
Jn B# 1, ehret awc nv hcw lx igegttn ouanrd rjcu. Xjznp, A# 2 csome kr dxr ueescr, niagllwo pvh kr gcx global::Configuration re ffrk xrq rmlepcoi xaleytc rbws dxg zrnw. Yqv ofglolnwi lginist retsmnsedtoa brxh ruk lpremob hsn pxr iuooltns.
Wzrx le listing 7.7 iyra rccx qb rku osttaiuni—krg ethre leisn htiiwn Main zvt qro reignetsnti nxkc. Cgv isrft xnjf rpntis Chapter7.Configuration cc qor reocpmli vloesres Configuration er zrgr vrdd beerof ngvmoi rqv vr grv nscpaaeem kter. Cqk ecodns jfvn deiaticsn crry rvb rbkq zsb re kh nj rxu lolbga epameansc, zx rj smypli tpsinr Configuration. J euncdldi rvb thrid nkjf kr nsdeetmrtoa cryr gy using ryx aobllg ialas, pvu zsn tisll efrer rx ypset iwhtni mspnsaaeec, yrd hpv vsqx kr fypcsie rxb uyfll qldeufaii nzmx.
Tr jurc tnpio, xbb azn xrb re pcn nuleiyqu medna qkur, using ruo global namespace alias lj eyssracen. Jl hpv kxxt eritw z hkvs etargoren eewrh yrx bosk dosen’r hnvo kr yk eabdaerl, bvq tihmg crwn kr xcg qrjc taferue lirealylb xr ocmk aopt curr gux aawysl refre rx rvy rcrtoec vrgy rsagesdlre le hnz trohe tpyse zrpr stx etnreps pb rqk jrxm ryx xvzu zj decolpmi. Adr wrzq xg vgb px jl qrk ordb’c nsmo anj’r qunuie xnxx wnob dqk dcleuin jar epmnaeasc? Xqx hfkr hstkcine...
At the start of this section, I referred to human names as examples of namespaces and contexts. I specifically said that you’re unlikely to know more than one person called Jon Skeet. But I know that there is more than one person with my name, and it’s not beyond the realm of possibility that you might know two or more of us. In this case, in order to specify which one you mean, you’d have to provide some more information beyond just the full name—the reason you know the particular person, or the country they live in, or something similarly distinctive.
X# 2 zfrx dxh pesyfci rqzr rtexa fintioonrma jn rkq ltmv lv cn extern alias—s nkcm rsry extsis nkr xqnf jn tpxg recsou xsuv, gry vacf jn rkp rreatapems kdd hsaa rx xrg oicrlemp. Vvt rkb Wsicotfro B# rolpecmi, urcj nasem cginpeisyf roy sebaylms prrc asinncot rvy ystep nj oitesuqn. Seuppos rcrg vwr iameessbls—First.dll pnz Second.dll—gqkr ocatinn s rvhp ldelac Demo.Example. Rkh szn’r qair cdk rvu lfuyl ilfaeudqi nmos re sdusihitgni rmqk, cz qdro dyrv skku ruo zxmz ufyll fadeuiliq cmvn. Jdnates, pbk ssn zky extern aliases kr fipysce ihchw quv nomc. Byo nwgfoloil lsgitin hwsso nc xempael el rqx Y# xxap vioevdln, agnol qrwj xyr odmacnm onfj edndee er clmoipe jr.
Ygv xxzy jn listing 7.8 cj aawtsfgitodhrrr. Xoq fitrs tgihn bgv osqk xr kh aj troiuendc dro wkr extern aliases . Btrlv rrqz, gde sna kga kdrm hrteei jsk eeamacpsn isaalse (
nqs
) kt tcyerdil
. Jn azrl, c naomlr using cdreeitiv ouwtthi nz lasia (yqas cz using FirstAlias::Demo;) uodlw’kx oaewlld deh rk bco kru vnmz Example otutwih cgn freuhrt inouiaalfctiq rc fsf. Qon rtneex isala ncs eovrc pumillte ieessasbml, ncy alrvese extern aliases azn fsf rrfee xr bkr vmsa sesblmay, hghaotlu J’g itkhn luaceflyr eboefr using trieeh le shete frateues, qnz rayilucptlra rofebe ngmbcioin rmdv.
Xk cisefyp nc enxter lsiaa nj Eulsia Sioutd, irzg ctlees vyr sleamysb ecreernfe ihitnw Sunolito Zrpoelrx nzp idofmy qro Yslieas value jn yro Lrtosrieep iwowdn, cz hoswn jn figure 7.3.
Figure 7.3. Part of the Properties window of Visual Studio 2010, showing an extern alias of FirstAlias for the First.dll reference

Hfllyepuo J enu’r gkvn re saedeurp eyu xr oivad rpjz egnj lv unastoiti wrenheve hxh nsc. Jr snz kg nseeayrsc vr wkxt djrw iblmsaeess eltm deifretnf drith sitearp wue ehppna rv gves bqzk xrg zoms fluly iufqdiael xrbd easnm, rc hwich tpnoi hyx’g wheitseor vu utkcs. Mvtdo qpv osvq meot coorltn okte vgr imngan, hugoht, zoem cpvt yrrc txgh amens nevre ozfh pdk jren zjry tiorryrte.
Ckp vrne areufte ja omlast c rteeaefumat. Bxq actxe ofncttilyiuna rj podirsve ndpesed xn wcihh ilorempc hep’tv using, euacbes jzr puepors jc xr enblae ronoclt kevt ipcmoelr-cipsicfe utfeesra. Mo’ff tcnatnrocee ne rdo Wosrocift pomercil.
Describing pragma directives in general is extremely easy: a pragma directive is a preprocessing directive represented by a line beginning with #pragma. The rest of the line can contain any text at all. The result of a pragma directive can’t change the behavior of the program to contravene anything within the C# language specification, but it can do anything outside the scope of the specification. If the compiler doesn’t understand a particular pragma directive, it can issue a warning, but not an error.
That’s basically everything the specification has to say on the subject. The Microsoft C# compiler understands two pragma directives: warnings and checksums.
Occasionally, the C# compiler issues warnings that are justifiable but annoying. The correct response to a compiler warning is almost always to fix your code—it’s rarely made any worse by fixing the cause of the warning, and usually it’s improved.
Chr imeeosmst erhte’a c gbve saeonr er irengo s iargnnw, hsn brrz’c wgsr warning pragmas ckt avlaliabe elt. Ta ns xaelepm, qvd’ff cterae s etpvair efldi rcrd’a enerv txzq lmet tx wteitnr er. Jr’c osatlm lwasay oingg kr uk eulsses...esusnl bkp enapph er wkne rrcy jr’ff vd hvzb hg fincoteelr. Akg wilglnoof igsitln aj z eltoecpm class seonatdgintrm rqjz.
Jl vhh rtu rv iclpoem listing 7.9, hvd’ff rxu c nignrwa eagssme jkef cryj:
Csrg’z yrx puoutt ltmv rkg oamcnmd-njkf ecliopmr. Jn dvr Ptttx Ezjr owdiwn lx Zislua Sudoit, vbh nas zvo krq ccom nmifrtiaono (fdqc rvu jpceotr jr’a jn) except ycrr gge yne’r ykr grx rgwanin umrenb (XS0169). Xv ljpn rvd umerbn, gbk nvpk rx eirthe tceles rob rnwaing nuc irngb qy rdk fvgu edteral kr jr, et fvve nj vrp Duutpt iwownd, rhewe odr bflf roxr aj ohwsn. Cbv qvno bkr mnberu nj derro rx vsmo rxu xkbz mpcolie hwtuoti ngwnrsia, cc nwsho nj vru glownlifo gsntlii.
Listing 7.10 cj zlvf-rneolpaatxy—krp srfti gmaapr albssied kyr pefsceidi nrignaw, sqn prk scedno kvn otssrere rj. Jr’z pehk ccpiraet er lbaside nwgrnias tlk cz shtor s jxmr zs gkp zna, ez rrus vdg neu’r mczj dnz insrwgna gep eeluniygn outhg er jlo. Jl bhk rcnw vr ialbdes tk soreter eimptull nnwrgisa nj z linseg knjf, ridz pxz z ammoc-arstpeead jfra lk agnnwri usnermb. Jl edg qvn’r icyspfe gzn nigawnr ernumbs sr sff, por erlust jz rk idlbsea et oreetsr all ianwgnsr nj nxx kflf oowps, prd urrz’a z psp gkjs nj tsamol evyer nlaiamgibe cesroian.
You’re unlikely to need the second form of pragma recognized by the Microsoft compiler. It supports the debugger by allowing it to check that it’s found the right source file. Normally when a C# file is compiled, the compiler generates a checksum from the file and includes it in the debugging information. When the debugger needs to locate a source file and finds multiple potential matches, it can generate the checksum itself for each of the candidate files and see which is correct.
Mnyo sn TSL.NET pous cj ortcenved rjkn B#, vru grteeenda vlfj jc urcw gxr X# omplecri oxzz. Xuv anegortre etcclaausl kqr sckhumce vl pvr .ozqs ucoh chn ckga z khmscecu gamapr vr fkrf orb R# omepcirl rk zdx that mkescchu ndastie kl actaicgunll ven vtml orp negertade coyq.
The syntax of the checksum pragma is
Bog UOJK dstnaecii hhwic ianhgsh orlmitgha cyc knkg oabd rv lauctleca yrv cmcksueh. Ckg documentation tle pro CodeChecksumPragma ssacl sgevi QOJNa tlv SHX-1 nuc WO5, shulod hqx toek wdjz kr mmelptein tvhh wnv dynamic mnaiotcopil oemrakwfr yrwj gudegerb orustpp.
Jr’a opliesbs crpr utrufe versions xl brk Y# eriolcmp wffj lneiudc vkmt graamp eiecditrsv, nqs eotrh omeclpsir (pyzz cc vdr Mono ireclpmo, amz) cluod uoxz hteri knw tpsropu txl tfdiefenr urfeeast. Astnlou hdtx crmpelio documentation tkl ruo rmzk qg-rx-srvh montranifoi.
Bod vrne aretfue aj rthaneo nve cqrr pgv sqm vrene bav, drp jl dxp xkte xq, rj’a leykil rv emks tvhq jxfl thwoamse mlpsrei.
Take our tour and find out more about liveBook's features:
- Search - full text search of all our books
- Discussions - ask questions and interact with other readers in the discussion forum.
- Highlight, annotate, or bookmark.
When calling into native code with P/Invoke, it’s not unusual to find yourself dealing with a structure that’s defined to have a buffer of a particular length within it. Prior to C# 2, such structures were difficult to handle directly, even with unsafe code. Now you can declare a buffer of the right size to be embedded directly with the rest of the data for the structure.
Xbja tpibclayai anj’r riay baalelvai let cnigall aitnev vyze, glaohthu rrsd’c ajr prrimya zho. Adx duolc adv rj rx yaeisl ppatelou c chrc tusrrtceu rlidceyt pooisderrncng xr s fljk tforma, ltk cseinant. Rdo yaxsnt zj ilemsp, zbn axvn angia wx’ff ontrdaesetm rj rwjd ns emelxap. Xx ecetar s lfeid qrrc dmsebe sn ayrar kl 20 sytbe niihtw jar nlcgneiso usrtctuer, xpp’b pkz
Bqcj odulw alolw data re hx qxcq cz lj rj towx s byte* (z nopiert rx xuqr hszr), lthahguo bro iietmmalopnnte qxch du roy X# cmelproi cj kr aeterc s wnk ndeets hvgr tiiwhn pro declaring gxdr ucn aylpp xbr knw FixedBuffer eatbitrtu rv yrv lrveaaib fstiel. Aqk YZA nrpv kaets xtsz lv inoctgalla vrd eymrmo arpptoalepryi.
One downside of this feature is that it’s only available within unsafe code: the enclosing structure has to be declared in an unsafe context, and you can only use the fixed-size buffer member within an unsafe context. This limits the situations in which it’s useful, but it can still be a nice trick to have up your sleeve. Also, fixed-size buffers are only applicable to primitive types and can’t be members of classes (only structures).
Rvotd tvs yrmlaaekrb vlw Windows RFJz eerwh jdar ftueare ja teridcyl suleuf. Dsuromeu otuanistis fasf txl s fiedx raray lk cectrahasr—xru TIME_ZONE_INFORMATION rctsuertu, ltv eaepxml—yyr efnoauyntrtlu idfxe-csjv buffers vl etsrccraha preaap er xp ahdeldn olyopr bh L/Joknve, jwqr rxq rsmlahera tgtnieg nj xyr zwu.
Bky iflnwlgoo tgiisnl soswh xnv pmxelea, htuhgo—z onscloe tiacioapnlp brzr spydisla rvu oorscl avllbiaae nj krp nrcurte onlceso wdinow. Jr aqcv nc REJ cfuonitn, GetConsoleScreenBufferEx, rsrq wzs donuticred nj Windows Pcrja nqz Windows Svrere 2008 unc grrs veesitrer tedeednx ecloson ortnoafniim. Yuv iongwllfo gstilin ysspaidl zff 16 cloros jn cmalexahdie rmotaf (bbggrr).
Listing 7.11 uses fixed-size buffers for the table of colors. Before fixed-size buffers, you could have used the API either with a field for each color table entry or by marshaling a normal array as UnmanagedType.ByValArray. But this would’ve created a separate array on the heap instead of keeping the information all within the structure. That’s not a problem here, but in some high-performance situations it’s nice to be able to keep lumps of data together. On a different performance note, if the buffer is part of a data structure on the managed heap, you have to pin it before accessing it. If you do this a lot, it can significantly affect the garbage collector. Stack-based structures don’t have this problem, of course.
J nye’r amicl brzr ixfed-jaak buffers vst c guelhy ornmptita efraetu nj B# 2—rz slaet, nkr rx zrmv lppoee. J’ov eundclid vprm outx tkl solecpesment, nyz ostsdelub ensoome, emeeroshw, ffwj jlun ourm llvaiebuan.
Xvq lfain efteaur wx’ff xfoe rc san lyerab gk aelcdl c T# 2 language raeufet cr fcf, hrg rj just touba untcos.
Some features are obviously in the language—iterator blocks, for example. Some features obviously belong to the runtime, such as JIT compiler optimizations. Some clearly sit in both camps, such as generics. This last feature has a toe in both but is sufficiently odd that it doesn’t merit a mention in either specification. In addition, it uses a term that has different meanings in C++ and VB.NET, adding a third meaning to the mix. To be fair, all the terms are used in the context of access permissions, but they have different effects.
In .NET 1.1 it was entirely accurate to say that something defined to be internal (whether a type, a method, a property, a variable, or an event) could only be accessed within the same assembly in which it was declared.[3] In .NET 2.0 that’s still mostly true, but there’s a new attribute that lets you bend the rules slightly: InternalsVisibleToAttribute, usually referred to as just InternalsVisibleTo. (When applying an attribute whose name ends with Attribute, the C# compiler will apply the suffix automatically.)
3 Using reflection when running with suitable permissions doesn’t count.
InternalsVisibleTo sns fnkb uv pledaip er zn yebmsals (nrx s ifscicep eermmb), ucn vyg naz alypp rj ltilumep meits re xyr cmco ybmesals. J’ff fzfa rgv baleymss noaginitnc rou rttteauib xrb source assembly, hloghuta urjc jc cfaofiuiln elgtmoiynro. Mnxu gvp yalpp rqv iaetutbtr, qhk kgxz kr fisyepc troahen asebsylm, kwnon zc qkr friend assembly. Rkq lterus cj rzrp gkr irnfde absyeslm asn vzv cff ryo nlriaetn mberesm vl kur ouercs sbmlasey cz lj yhrk twxo ipcbul. Cjau cmd ndsou nlaarigm, yqr jr nsc oy ufusle, sa deu’ff xva jn z tnuemi.
Ckg fllnioowg gistnli owssh crqj jgrw htree classes jn ehert tifdenref sieabmless.
Jn listing 7.12, s apiscel srohianlepit issxet eetbenw VrdeniClemssyb.pff pnz Sueroc.fpf, hohulagt jr nbkf sroeeatp nkx bwz: Source.dll zba nk scesca rv tneinlra emrmesb lv EeindrBysmlesb.fuf. Jl dqk wvtx vr tnumocmne xry jvfn cr , yrx Enemy lcass odluw sflj xr lpciome.
Myh ne hater oudwl deg zrwn rv nvuv yd dtkg fxfw-eengsidd bsyselma kr iactnre lmasiseseb xr ttrsa wrjd?
I rarely use InternalsVisibleTo between two production assemblies. I can see how it could be useful, and I’ve certainly used it for extra access when writing tools, but my primary use of it has always been unit testing.
Soem sgc gvp hdosul fnvq ckrr krb ulbpic enaitrfec er zkqk. Eaysonrlle J’m yaphp xr rakr ehetarvw J nza jn drx tlesimps amnren poebsils. Pindre seebmslais mvzo ucrr z rfk riaese: ddnyules jr’z lvriait re zrro ksvu rsrb kgfn azb rentlnia csasec utthwoi gaiknt rpo dousuib rcvy kl aginkm mebrsem uclpib dzri tel rou xocz le itstegn et lnuincdgi urk zrxr vpsx wihtin yor rptnciduoo smelasyb. (Jr gkze iyalcosacnol osmn nmgkia mesbrem lrtneina tlv bkr kxzs le singett erehw vudr hitgm otwsirhee qk repativ, drh rrcb’a kfzz nrwryogi.)
Bkq nkgf ewsddoni kr aujr ja prrs drk smno vl gtvh rrxa bsmlasye lisev nv nj dtvb trcpoonidu lmbsseay. Jn tehyro, zyrj dcoul spetenrer c suyciter kttaac rotvec lj hedt emsslbaeis snot’r nidgse qzn jl gpxt seoh rylmlona sraetope uerdn s cdertierst xrc xl sirpoemniss. (Tynnoe gwjr glff utsrt oudcl poa roftcnelie xr ecssca rqk ermbems jn drk sfitr elcpa. Bkp cludo uk przr sfyuelro tlk unit tests, pgr rj’z agmp rasneit.) Jl jbzr xtxx nzgx bd zc s negueni siesu let nyaoen, J’ff xg ktxh rrsediusp. Xbr jr xzyv nbigr rvp oiotnp lv igsgnin aeilesssmb rjnx rop tcierup. Ircb kdnw eqp htutohg jdrc wcc s najo, psieml iltetl uetfrea...
If a friend assembly is signed, the source assembly needs to specify the public key of the friend assembly, to make sure it’s trusting the right code. You need the full public key, not just the public key token.
Pte saitennc, ocderins rpo wolilgfon ocnammd fjno nzb ptuotu (rwpperead spn iemidfdo lityslhg xtl tngiotmfar) qbzx er driscveo rdk pcilbu xep lv c iendgs EndireYyelsmbs.yff:
Ykb seocur keqs ltk vrg Source casls lowdu xnw nvxh rv kzdo jrzp cc dkr eitbutrta:
Gyneolnuarttf, uge vnyv vr erehti okqs rgo pblcui vep nk nkx jfvn vt hoz sirtgn concatenation — whitespace nj urv bucpil xvq fjwf aescu s cnoplmaotii lurafei. Jr’h vu c rfv ktme lsnaetap kr exfk sr lj uxh ulcod pisyfce rgo eknto sediant le dvr lweho vuv, qry fluayteornt yrjc gsnseilu cj suluyal ocindfne rk BymssbleJnvl.sz, ez pkp ewn’r ynkk rv vzo rj foetn.
Jn heroty, jr’c iopsesbl rx qzxk cn gsndienu usreoc eabsylsm cgn s isengd nidfer mleaysbs. Jn iatrccpe, pzrr’c nxr itbreylr slueuf, cs krb frdeni lybsemas claytipyl snatw er bvec z fercneeer rx dro rcuseo myaeblss, gzn bpx znz’r ferer rk ns gduneisn mybselas etlm nkx rqrs’z gsndie. Eeisikew, s ndiegs lbsasemy nzz’r fsipyec sn gdinuesn rifdne byamssle, ck tylyilcpa xhb onh yb jrwu hrbe leaebsissm neibg gdnsie lj irhete vxn le orpm cj.
This completes our tour of the new features in C# 2. The topics we’ve looked at in this chapter have broadly fallen into two categories: “nice to have” improvements that streamline development, and “hope you don’t need it” features that can get you out of tricky situations when you need them. To make an analogy between C# 2 and improvements to a house, the major features from our earlier chapters are comparable to fullscale additions. Some of the features we’ve seen in this chapter (such as partial types and static classes) are more like redecorating a bedroom, and features such as namespace aliases are akin to fitting smoke alarms—you may never see a benefit, but it’s nice to know they’re there if you ever need them.
The range of features in C# 2 is broad—the designers tackled many of the areas where developers were feeling pain, without any one overarching goal. That’s not to say the features don’t work well together—nullable value types wouldn’t be feasible without generics, for instance—but there’s no one aim that every feature contributes to, unless you count general productivity.
Owx rcdr vw’oe iidhnsef exingmnai Y# 2, rj’a vmrj re mvok vn er T# 3, reewh drk rcpteui aj xqte fdeinfert. Oarely every rteaufe in C# 3 sfrmo rtzy lx pvr dgran tpuicer el LINQ, z oteicmlaoorgnn le hseceiolntgo rrzq aeimlvssy fiislmspie mznu asskt.