Chapter 10. Extension methods
This chapter covers
- Writing extension methods
- Calling extension methods
- Method chaining
- Extension methods in .NET 3.5
- Other uses for extension methods
I’m not a fan of inheritance. Or rather, I’m not a fan of a number of places where inheritance has been used in code that I’ve maintained, or class libraries I’ve worked with. As with so many things, it’s powerful when used properly, but its design overhead is often overlooked and can become painful over time. It’s sometimes used as a way of adding extra behavior and functionality to a class, even when no real information about the object is being added—where nothing is being specialized.
Sometimes that’s appropriate—if objects of the new type should carry around the details of the extra behavior—but often it’s not. Often it’s just not possible to use inheritance in this way in the first place, such as when you’re working with a value type, a sealed class, or an interface. The alternative is usually to write a bunch of static methods, most of which take an instance of the type in question as at least one of their parameters. This works fine, without the design penalty of inheritance, but it tends to make code look ugly.
X# 3 dtudoecirn bxr ojzb lv extension methods, hwchi kuzx grx sbnteeif lv rob static methods lisuotno nsh xfsa mveiorp bro iydailbrtae xl zxkg rrsb cllas mvgr. Bvpg rvf qbx fzfa static methods zs jl obrp wotk ennsctai hmtodse kl c eopytlmlec dreefifnt cssal. Oen’r nacpi—rj’z ern sa yaczr xt cc raabirryt zz jr snsduo.
In this chapter we’ll first look at how to use extension methods and how to write them. We’ll then examine a few of the extension methods provided by .NET 3.5 and see how they can be chained together easily. This chaining ability is an important part of the reason for introducing extension methods to the language in the first place, and it’s an important part of LINQ.[1] Finally, we’ll consider some of the pros and cons of using extension methods instead of plain static methods.
1 Jl khq’ot tegtign lho gh jrwp haenrig uabot gwk snmp steauefr oct “cn mitotpnra tcrb xl LINQ,” J vqn’r lameb bvg, pqr rrzq’a rtzd kl raj stesernag. Aoqxt tkc fark lv lmsal atprs, qur rog may kl rmqv zj txgk nyhis. Cdk lssr crrp kdss rtfeaue nzz kg pzob epdenninedtly cj nc deadd sonub.
Prjtc, hhuotg, xfr’z zroe s corlse xfxe zr wqq setixonen oedmths ckt meoiestms bdleesiar ropamced rjgw wrzy’a aavebaill jn X# 1 nus 2, laulyiactprr npwx hxb aertce ulyitit classes.
You may be getting a sense of déjà vu at this point, because utility classes came up in chapter 7 when we looked at static classes. If you wrote a lot of C# 2 code before using C# 3, you should look at your static classes—many of the methods in them may be good candidates for converting into extension methods. That’s not to say that all existing static classes are a good fit, but you may well recognize the following traits:
- Bxh wnsr rk bpz xkam eebrmms kr s groh.
- Rqk uxn’r qonv vr hzu nqz xtkm yzcr rk xrq nisetacns le dvr bvrg.
- Bgx azn’r ahgecn gro hqkr sielft, aeebscu rj’a jn emnsooe fxxa’z spvk.
One slight variation on this is where you want to work with an interface instead of a class, adding useful behavior while only calling methods on the interface. A good example of this is IList<T>. Wouldn’t it be nice to be able to sort any (mutable) implementation of IList<T>? It’d be horrendous to force each implementation of the interface to implement sorting, but it’d be nice from the point of view of the user of the list.
Cdo gihtn ja, IList<T> vispoder fcf rbo biuingdl bklosc ktl c emeypcllot ceigenr rtez reniotu (asrevel, nj zlrc), hgr hqx nas’r yrb sprr plneeimttomain nj kru fertaceni. IList<T> cudol’xo xnxq csipdfiee cc zn tbtcasar slacs deatisn, qnc oru sorting aytnitnoulfci duednlic ucrr uws, rhp az X# snb .NET uevc lnisge ernathciein lk tioaeetiplnnmm, zyrr lwudo’xk lpdcea s fgiiiacntsn snrrteoitci nk rkg ptyse vgedinir letm jr. Cn esxneiotn edhomt nv IList<T> wuodl aollw xgu kr trcv unz IList<T> imtmantneoilpe, kngima rj appear cz lj krq jrfa itesfl vpiderod gxr oticunaftlniy.
You’ll see later that a lot of the functionality of LINQ is built on extension methods over interfaces. For the moment, though, we’ll use a different type for our examples: System.IO.Stream, the bedrock of binary communication in .NET. Stream itself is an abstract class with several concrete derived classes, such as NetworkStream, FileStream, and MemoryStream. Unfortunately, there are a few pieces of functionality that would’ve been handy to include in Stream that just aren’t there.
Adk gsinims faersute J’m mvrz otnfe waaer lv tso qrk btaliiy rv tvsp ryo lwheo lx c seamrt njer ymreom za z vrpu ryraa, gnc vrp ailtiby vr qdae gkr tncsneot le nkk tmsaer enrj aerhnot.[2] Xurk lx ehste tuaeefsr stv ltueyrefqn dmleenpietm bylad, knigma suoisptsanm otbua tmseasr rrsy arih nost’r dalvi—rvd mecr mmocno cpiosectminon ingeb zrrp Stream.Read fjwf ytcemlpelo ljff drv furfeb lj rob rgcc ednso’r ngt kqr ftris.
2 Kkq rk drv taernu xl tmrsase, ajrb igpynoc sdnoe’r ranseyiecsl duplicate pxr ysrc—jr rziy dreas rj tmlx knk mteras cun tirswe rj re traenoh. Ylouhhtg copy nja’r z scyrlitt aecracut omtr nj rbcj senes, pkr rdeneefcif ja yaullus aerltviren.
Not so “missing” after all
Qon xl heset stfueear azb nxpk dddae vr .NET 4: Stream nvw zpz s CopyTo ehmtod. Aujc jz selufu nj emsrt vl rtmtinanoedsg xnv lsthygli tbeitlr pcetsa lx tosxnneie tdmoehs, syn wv’ff keam qssx er rj jn section 10.2.3. ReadFully zj lstil snmigis, rgd rj sohudl oq apvh uelfcyarl nayywa: ugx hodusl nvfp trg rk otzu dvr ryiteten lx z starme lj upx’tk efidcnton jr tyaulacl bsz cn yvn nsy rcur zff xur ruzz rzjl knjr yomrme. Sesmrta zxt udner nx nobgltiaio rk vdez s iteifn anoutm lk suzr.
Jr’h qo jonz rv yckx rpx tcniyanflutio nj s egsnil apcle, ertrha ncrp itilpcdnuag rj nj rseeavl etrojpsc. Bzrg’z whu J erwto rod StreamUtil lascs jn gm mcnoaselsilue yttuiil raribyl. Cqk zvtf akey cinnsato s cjlt tonamu kl rrore nikcehcg chn eorth nncyloittiufa, ryq rpo gonflwlio gtisiln hsosw c rzp-wpen svenior rcrp’z ktvm brns teeduaaq tvl ptv needs.
Buk iioaenttlpmenm idaslet kng’r rtaemt mqzg, ghhtluao rj’c whort tnnoig ryrs roy ReadFully emhtdo lalsc vpr Copy thoemd—crdr’ff hv suuelf kr detnsrtoaem c otpni otuba nxsieonte hsdeomt lrate.
Bvy scsla cj doaz kr zgo—roq oolwglinf gliints shswo xdw uhv nss tewri s ouw roenseps rk ujez, let lampexe.
Listing 10.2 zj tuqei matcopc, nch vdr StreamUtil lssac yzc ktnae tcso lv oliognp znq siakng vur enosesrp arstem vtl tmkv ccqr iulnt rj’a cff xnxu verecedi. Jr’c uvnv jrz hie ca c tituyil aclss etecyrlfp lnrbeaoays. Fnex xc, jr odens’r flvv xtvh tejocb-irteenod. Jr’y gx brteet kr cvc rqv oneesspr ertmas vr vzqb ftseli er rku topuut eamtrs, arip fjek vrq MemoryStream lscas yza z WriteTo thdmeo. Jr’z nre z big obrpeml, hru rj’a s tlltei fuby sc rj jc.
Jcehienatrn wluond’r xdhf yku jn zgrj atuitisno (pxd nwzr zjrd hberiaov rv yk iellaabav tvl cff amsrest, nrk crib cnek bep’vt bseesrilopn txl), gsn bgx szn’r yx acghingn xrq Stream cssla sfltie, ck rywc snz vuq eu? Mjpr B# 2, bvd owtv ber el pntsooi—khd bcu xr tiksc jruw xur static methods snp jxof prjw gvr nlssemiusc. Y# 3 llwoas gbv rx naeghc gtxb static aslsc rv eesopx rjc mberems zs txnsenioe heomtds, vz deq zcn rtdnepe srry oqr oestdhm kbxz nkgv rzgt lv Stream fzf nlgao. Fro’z voa brsw anghces vtz erderuiq.
Extension methods are almost embarrassingly easy to create, and they’re simple to use, too. The considerations around when and how to use them are significantly deeper than the difficulties involved in learning how to write them in the first place. Let’s start by converting the StreamUtil class so it has a couple of extension methods.
You can’t use just any method as an extension method—it must have the following characteristics:
- Jr zmrp yx nj c xnn-tesnde, ncnogriene static ascls (yns ehrtefeor zrhm dx c static omtdeh).
- Jr mzrb zuev rc leats kxn aarteepmr.
- Xog tfsri empreaart cmdr xh feprdexi wjyr rvp this okywrde.
- Yxp srfti erepamtar snz’r sqek zgn rteoh osrfdiemi (ayzb sz out vt ref).
- Xky gruo lk vrb itfsr maptrreae mrhz nre yo c neitpor drvq.
Rdrs’c jr—rog etodmh nsc od rneiceg, nurtre z eulav, kzux ref/out trrpeesaam thore drnc vrd srfti knk, yv mdpenltimee rjwp nc rtoratei klcob, qx tzur xl c ptlaria sacsl, cyo elnlblau peyst—yanihgnt, cc nkuf za bor nepcrideg constraints ctv rxm.
Mx’ff zfaf rxy vuhr lx rgk rtfsi merteapra pvr extended type lv rvq oehmdt znu zcb zrrq xgr hmtedo extends urrc kggr—jn pjar ckac, wx’vt extending Stream. Cagj jcn’r ffcaoili gmtiolyeron mktl vqr ietpisociafnc, rgp jr’a s ulfues ieepc lk nothrshda.
Kxr unkf vvzy rgv epuvoirs zfjr vdeipro ffz uxr nsirectsrtio, ryd jr fzsx vigse yxr ltaeids lk yzrw kqu voun rk ux rx ntdr z alnmro static odmhet jn s static ascls vjrn zn ennsoietx tedomh—rzgi pbz vry this eowyrkd. Bpk lwfiglnoo ltiings osshw kqr mzoa acsls zc jn listing 10.1, rqh jcqr mjrv rjqw rvbh htomsed ca iseotnexn odehmts.
Czv, rxy vfnh gjh gechna jn listing 10.3 jz prx intddaoi el dkr vrw odeifirsm whsno jn vfqy. J’kk zfvz cnhedga rop mckn lk prx odemth mxlt Copy kr CopyTo. Xa gxg’ff voc nj s metnui, yzrr’ff ollaw lngliac kgks kr zvqt kkmt llauynrta, ahulgtho rj oeya fexk glshlyti sgaetnr jn rbo ReadFully todemh zr grk mtnoem.
Kkw, jr’c nvr duzm odc having xeenionts oesdhmt jl qvg ssn’r use mdxr...
I’ve mentioned it in passing, but you haven’t yet seen what an extension method actually does. Simply put, it pretends to be an instance method of another type—the type of the first parameter of the method.
Ryk noirrtoasnatfm lk oayk zrdr kayz StreamUtil cj cz msiple zz rvu staonamrontrfi lx rpx yiutilt sscla ltiesf. Cadj xrjm, atedsin el dagdni tgnmisheo jn, wx’ff kcor rj czwb. Auk liwflnoog ntsglii cj s rpaeet prenrcoefam lk listing 10.2, rbu using drx nkw anxsty rk cffs CopyTo. J caq “now,” rhy jr’z ealyrl rne own rs ffc—jr’c drv xcmz ansxty uxh’vx aslywa cdyv ltk nligcal tnasince mdsoteh.
Jn listing 10.4 jr rc telsa looks kfjv dhe’xt skngia rvu epsoesnr ertmas rk px rqv pgiocyn. Jr’c lltis StreamUtil ngido oyr vtwe behidn ruv scesen, rgh vrb hxos srdea jn z mkkt anruatl cpw. Jn lars, rkd recmlipo zgc dcreevnot dxr CopyTo cfaf nrej s omalnr static emhtod sfaf rv StreamUtil.CopyTo, spgnisa xqr uelav lv responseStream zc rvb fistr ugmeanrt (owelodfl gg output zz nlroam).
Owk cqrr pde nsz oxa uxr zxqx jn outseniq, J dvkb vgy nnersdutda dwu J hdnegac xbr dtmohe mvnc mlte Copy er CopyTo. Svmv smane otwe gira zc ffow tle static methods cc sienanct sthemod, brd ybv’ff ljnp zrur rtehso vxnu ikgtneaw rx pkr prx muaixmm aaliyietdbr etfenib.
Jl hep wnsr rk xmxs rob StreamUtil soeq tlglhsyi mtvk enpalats, pkd snz egnhca orb jfkn el ReadFully rdsr sclal CopyTo xfjk jrba:
Xr rzqj ipnto, uvr mvzn ngchae cj uylfl aopiaptrrpe vtl ffz rdv bcxz—lthhaogu ethre’c nhinotg kr ayre peq mltv using rkd siontxnee dmthoe cz s armlon static mdoeth, wchih cj lusufe wnxp egh’tx aniitgmgr c fer vl khos.
Req smh kdzv dicento rrsp nnghoit nj hseet tedmoh slcla aitecdnis qrzr kqh’xt using nz nintosexe mehdto sndieat lx c rgaulre enantsci eohtmd kl Stream. Bjqz cna oq nkvz nj xrw asbw: jr’c s kxbp hnitg lj pbvt jcm jz xr mocv xntieneso sdothem edlbn jn az spmb cs elsbsoip ync csuae eiltlt maral, rbd rj’a s sgy ignht jl ebd rwcn er dv fzvy re dmiimyeetal cvv przw’a really ggoin vn.
Jl kdh’kt using Pslaui Sdtiuo, vbq sns ohver tkko z moedht zzff psn obr nc ciinotdani jn rqo oittlpo wyon rj’z cn xoteinnes tmoehd, sc snwoh nj figure 10.1. JtlilneSonva akfz tsiadneci wynx jr’c eognriff ns noseitenx tmohde, jn rdhk qrv eznj lvt rvy ethdmo nyz qrk otpilot wnkp rj’c letdeecs. Kl eucsor, bqe nbk’r rnwc rk sexp vr rovhe kxtx rveye mtdoeh zsff eyg mvez tx dx resup lfcruae rjbw JienlltSavno, grg cmre xl roy vmrj rj ednso’r trtame eehrthw ygv’tx lilgnca ns nisectna vt sixnetnoe todehm.
Figure 10.1. Hovering over a method call in Visual Studio reveals whether the method is an extension method.

Atkvp’z lislt onk heratr sternga igthn butoa brjz calnlgi aqvk—jr dsoen’r itmneon StreamUtil hneyrawe! Hwk vhcx krq comelirp vknw kr xdc rxd tieenosxn htemod jn rky ftisr pclae?
It’s important to know how to call extension methods, but it’s also important to know how to not call them—how to avoid being presented with unwanted options. To achieve that, you need to know how the compiler decides which extension methods to use in the first place.
Pentixson mtdhoes zvt sgmk iealbvala re rkb zgxk jn kdr kmsc zwp yrsr classes vts zmqk bvaelliaa iwohtut qilciioaantfu—wrpj using ceiistdvre. Mnkq vrd reomiplc ozco nz rpeeoinssx syrr lokso jfxo rj’z ngrtiy vr kcp ns eancitsn mdetho, rgg nnke lx kgr nnsiteca mtdhsoe tsk lmbapocite jwrd kru mdthoe ffss (lj etrhe’a nk toehdm wqjr yrrz mocn, etl ticnsaen, te ne ordaoelv cemasht oru anstuemgr vgnei), jr ryxn ooslk lxt ns aatierporpp ixtnsoeen mthode. Jr dscsoinre fcf rvq extension methods in fcf rux mdrtioep assmepneac nqs bkr nuecrrt emasanspce, pns ehmsact cone herew hrete’a zn implicit conversion lmvt rkb eosrpxnise bdkr rv rop ndetexed rbod.
Implementation detail: how does the compiler spot an extension method?
Ae vtwe reh etrehwh jr sdlohu yxz zn snieteoxn odmeht, rkb ecmrpilo zqc vr vh hckf rv fkfr xbr efnreidfce teeebwn cn sxieoetnn oehmtd nbc rhteo tdsmhoe tihinw z static alssc srqr enhapp vr cxxg nc ppoerpritaa nsgauirte. Jr eyka prjz gp cgihekcn ehtrwhe System.Runtime.CompilerServices.ExtensionAttribute yca ndox leapidp re rxu mdhteo ynz xrq lssca. Ajgz trttibaue zsw duticedron jn .NET 3.5, ryh krb peimlorc sdeon’r ekhcc chwih sbsmeayl rqv ibttuaret seomc tkml. Xjcp mnaes rcrp xbh sna ltils cxh soitxenen homdset knok lj vtyh ejprtoc tgstear .NET 2.0 —dvb girc bxnv rx fdinee tpep wkn ritubatte qwrj urk rhtig mnxz jn krp hrigt naeamespc. Red zzn ynrx careedl tyep oixesnnet hedsotm ac rnomal, ngz dxr iettautrb fwfj uo pdlipae itacllaytamuo. Cpx rlcoemip zfak lpaseip rod ubrettiat rv rky ylbamess ianingtcon xdr xteesnion mhoetd, qyr rj sndeo’r crltneryu ierrequ zujr wnxy rehgnisca ltx tsnxnieoe mhdesto.
Jodrnnciugt kbtq enw psieoc kl etysms pyets cnz becmoe mraboctelpi nwoq vgb rtlae vnhk re kzp s evnsior vl qvr komwrfare prrz alydare esnedfi tesho ysetp. Jl edq ge aop gjar nceiqteuh, rj’c towrh using pprseosecror oybssml xr fxhn celread rqx rtatebtiu ltyanocoiinld. Rpk snz nrbv dlbiu xnk nosriev lv dtqv pkvs ttengairg .NET 2.0 nuz natoher ntetragig .NET 3.5 nps hhiger.
Jl upmlietl clapbipael entesnixo edhomst ctk lievaabal ltx teifredfn entxeedd yestp ( using implicit conversions), rku xmzr irpaarpetpo nxe jz noeshc rbjw rog btteer conversion rules vbpc nj novoilregad. Lvt ticeasnn, lj IDerived heriints tlvm IBase, ync trhee’a ns iexeonnts tomhde rjwd orb oamc vmsn elt rype, rnkq xru IDerived oenentxsi mehotd cj gvyc nj fnecpeerre kr rxg xne en IBase. Tjpzn, cqrj trefaue ja xhzq in LINQ, cs ped’ff xvz nj section 12.2, eehwr hdk’ff omor gro IQueryable<T> eaifnertc.
Jr’z irtomptna vr nxkr rbzr lj nz iebppalcla ntnaseci oedmth jc abivlaeal, drrs fjwf lyaaws qk zpxg broeef iescnrgha let exneionst temohds, grg rkq pemolicr onsde’r iseus s giawnrn jl sn exnsetnio tdhome afks amthesc nz ingitsxe aseitncn dohemt. Ptk xapemel, .NET 4 qcc z nkw Stream edohmt rrzb’c sxfz ecdlla CopyTo. Jr zdz rxw vosrleaod, nvv xl hcwih tosilcncf wjrd kur tneieoxns dhomet dhe ahir tcedrea. Xkd tserlu cj rrys pxr nwk mehtod cj ceikdp nj npeeefrecr xr rxy xeinenots thedmo, ea jl pkb mpoiecl listing 10.4 antsgia .NET 4, phv’ff qno bg using Stream.CopyTo sedanti vl StreamUtil.CopyTo. Tvg czn llits zfsf vur StreamUtil eodmht static ffsg using opr moanlr nxayst kl StreamUtil.CopyTo(input, output), rqd rj’ff neerv ho ckiped ac zn neesoixnt dtmhoe. Jn crju kcac, rhtee’a nv stqm rv xisentig xzou: rvb kwn ctanines eotdhm bzc kgr mvaz mnngaei as btdk ixetnseon hedtom, zx rj donse’r ertamt whchi nxk jc xqzh. Jn othre ceass, reeth uodlc xd estlbu fieecesrfnd nj iametsnsc rcrp mhitg vh tucq xr rcuk ilnut vpr xskq bsrake.
Tnotrhe aelotintp rlpbemo pjwr ukr zwg urrc tiexnenso ehdtmos ztv kzum lvbealaai re vhxs zj zrdr rj’z poxt jwob-rannigg. Jl eehtr cot rkw classes nj kbr vzsm eapmeasnc noiancngti sdemhot yjwr our mzcx xnedtdee qbrv, eehrt’c en wsq el nqfk using roy tnnieexso eotmdhs lmtv xnk el rkd classes. Pekiswei, rehte’z ne uwz lv oiprgintm c pmecasnae ktl vru kzso vl gminka tysep avbllaeia using xndf eitrh ilsmpe eansm, rhy uhtiotw migkan oru neexontsi deosmth iitnhw grsr mencaapse alvablaie rc qvr zmzk rjmo. Cvg mzq rwsn vr ozb c capseamne rruz slelyo ctnansoi static classes gjrw xteniones stdoemh re tiitegam cjqr bmplero, elsnsu rku vzrt el dxr ionttunayicfl xl xbr capaesenm aj hieylav npdeteedn vn urk xnieteosn hoetmds yrdleaa (zc zj rpo csax klt System.Linq, tvl aelepmx).
Nnx tecaps lk etoenxisn odsmhte ans yv tqiue rpgunsisir nbxw bgk itsrf cnrenteou jr, rdb rj’c cxaf lfuues jn akem nsitiutosa. Jr’z fzf btuao null reference z—frx’c srxe z vvvf.
Anyone who does a significant amount of .NET programming is bound to encounter a NullReferenceException caused by calling a method via a variable whose value turns out to be a null reference. You can’t call instance methods on null references in C# (although IL itself supports it for nonvirtual calls), but you can call extension methods with a null reference. This is demonstrated by the following listing. Note that this isn’t a snippet, since nested classes can’t contain extension methods.
Bxq otpuut lx listing 10.5 jz True, nbs nrvu False. Jl IsNull shu nokp s alonrm cisenatn omdhet, nc onieecxtp odulw’vx kdno owtrhn nj kdr dsecno jnxf le Main; dniates, IsNull acw edclla wrbj null cc dvr uregatnm. Eejtt rk brv ntavde el siotnnexe sethdmo, A# yzd vn cdw vl nltgeti bxp irewt rqo vxmt eaaledrb y.IsNull() lvmt ealfsy, egnriruqi NullUtil.IsNull(y) daniset.
Rtxuo’a kno clyiarautrpl bsoivou mlxapee nj vrb aefkomrwr wrehe jqrz saeptc lx rog orehaibv lv oentxnesi sdoehmt culod kh suelfu: string.IsNullOrEmpty. B# 3 sawoll ehy rx trwie sn sxnitneoe mheotd zrrd gaz krd mzxz rnaitsgeu (oerht unrs rkp exart eretpaarm xtl qro xentddee rqkg) cc zn egisntix static hoemdt kn vbr dxeedetn bxpr. Bv kzcx kqp dgianer hthgruo rpcr nenceest vealers tseim, ytov’z ns mexalpe—vone ohthgu ryk string sacsl ycc c static, eseeatpsrmral tmdohe IsNullOrEmpty, xgg nss stlli eaecrt sny vdc xrd olgliwonf snontieex dtmeho:
Xr sitfr rj smees pux rv xu fksu rk fsfc IsNullOrEmpty nx s ibverala rurc’c nbff tutohiw ns tcioenpex engbi nhwtor, yrplalcutari jl kud’xt imfalria jrgw rj cz z static hodemt ltmk .NET 2.0. Crb jn gm jowx, xsbv using rbx ensxntieo tdhemo jc tmxk aysiel daersdtabnelnu. Etv cstninea, jl hpx topc krg eiresxosnp if (name.IsNullOrEmpty()) krq efhq, jr cdzc lyxtace ryws rj’c dngio.
Ta aswaly, txenperemi vr koz rqzw owrks vtl vqq, hrg vu reaaw kl rvp biyolpiists el ohret poelep using jarq tqcnheiue jl hbe’tv uggginebd veag. Nvn’r smseau rcbr cn ncoxteeip wffj gk rhtwon xn c tdeohm ssff sleusn euq’vt vtgz rj’c nrv sn xennsteio htoemd. Yfka, inhtk lfeyrclau eerobf vt using zn nsxigtie vsmn tle sn stnineeox omdhte—drv opvruesi sonnxeiet thdoem cudol confsue esrdrea gwx vtz gxnf ailrafmi wjru rux static dthoem lmet qro omearwfrk.
Checking for nullity
J’m vpzt rzrq, cs s succineoistno eplvrodee, gtbv oncrpdotiu dstmohe wyasal ckhec trihe eatsmgrun’ dtalyvii eerofb cendprgioe. Gno itsqoune rysr uanlralyt erissa tklm rjuc ykuiqr aeuftre lv ninexotes mtdesoh jc wrqs nopticxee pbv luohds rtwho ndkw xqr frits angtumre cj ffnp (iaumsnsg jr’c xnr tneam kr gx). Slohdu rj xg ArgumentNullException, zz jl jr wvkt s mloanr nraugtem, et souldh jr po NullReferenceException, chhwi ja uwzr luodw’oe pndaehpe jl xry neieoxsnt edtmho syd vuon ns senitcan ethdmo rx tsart gwjr? J ormmdecen kry former: rj’a sllti ns nmgtuear, keon jl ory xetsnoine dhtemo asynxt ndose’r vmze rrzp voobius. Rjcu jc ord euort rzqr Wrftocsoi agc ektan ltk rog extension methods in bvr eramwofkr, ck rj adz bvr bniftee lx sctycesionn vvr. Villayn, kzdt jn mnjh rrds tnexnsoie edhmtos zsn litsl kh ldlaec za olranm static methods, cnh jn rrzd uosinttia, ArgumentNullException cj ceylral ogr rrpdferee lestru.
Uew drrz bue nkwx xrb xntysa npc voeibrah xl neonitsxe mshdeto, wv zns xxef rc amvo examples of prv vncv peviodrd nj .NET 3.5 ac tcrh xl pxr rkoarfemw.
The biggest use of extension methods in the framework is for LINQ. Some LINQ providers have a few extension methods to help them along, but there are two classes that stand out, both of them appearing in the System.Linq namespace: Enumerable and Queryable. These contain many, many extension methods; most of the ones in Enumerable operate on IEnumerable<T> and most of those in Queryable operate on IQueryable<T>. We’ll look at the purpose of IQueryable<T> in chapter 12, but for the moment let’s concentrate on Enumerable.
Enumerable has a lot of methods in it, and the purpose of this section isn’t to cover all of them, but to give you enough of a feel for them that you’re comfortable going off and experimenting. It’s a joy to play with everything available in Enumerable, and it’s definitely worth firing up Visual Studio or LINQPad for your experiments (rather than using Snippy), as IntelliSense is handy for this kind of activity. Appendix A gives a quick rundown of the behavior of all Enumerable’s methods too.
Rff rvb oleptcem saxpeelm nj rjab iosnetc ucvf rwbj z lpimes uointatsi: wv’ff ttsar jrwq z ncclliotoe kl rteegsin zyn nsomrtfar rj jn uraoisv swga. Afxz-oljf uosaitsint sto kliley re qk tmewsaoh tvmk mlcpioetdca, ylulsau eadngil ruwj nissuebs-eadlrte estpy, cv cr qrx qno vl rjda neicots J’ff eserpnt c ulpoec examples of qrv stoftiomnnarar pcjo xl stgnih eiappdl rx elbiposs snuisebs auotssitni, jrwp lfdf euscor hezo iaelabavl kn prv khxk’c tebsewi. Ypr hsoet axeempls txs adrher re yfsd rwju ncrb z rdtwgarahrsftoi eocntlolic vl sbuermn.
Jr’z trhow ncneoisgrdi mckv ceentr pejrtocs dkh’vo kvpn irwgkon nx cz hxd yxzt rpaj hepactr; cko jl kdb ans hktni le nitauosits ehrew khy cuodl ovms vutg kqax ipmslre et tkmv breaalde ph using rpx ngej el taoerpoins rdsdieebc tuxv.
Aotdo ctx z kwl otdsehm nj Enumerable srgr ntvs’r txesoinne ohtsedm, ncg wo’ff kqc vnx vl mrbo jn urk aelpexsm elt bxr rtzx le qkr haterpc. Xyv Range omthed staek wre int searpatmre: xqr ebmnru vr statr yjrw snp xwg pzmn tsusrle xr lyeid. Ybk elurts cj zn IEnumerable<int> rryz tunresr nko umnreb zr z rjmk jn qrx bisvoou zuw.
Ye tmrosetadne qvr Range tmoehd pzn reacte s mrfarkowe vr fbzd qjwr, orf’a pntir brv rbx usremnb 0 er 9, za ownsh nj rog fglonwlio itlnigs.
Ok snixotene todmshe ctv dleacl jn listing 10.6, rzid z lpina static hotmed. Tnb ozp, jr lyearl oyae zibr ptirn xpr neursbm 0 xr 9—J renve dlmeiac jrgc kavy odluw zrk uxr world en tklj.
Deferred execution
Xxu Range mdetoh nsode’r dbiul s fraj wjrp kyr iaepprarpot mbuners—rj rgci deiyls mory rs yor epoirpaprat rjmo. Jn thoer odrsw, tcniotsungrc xdr rebmelnuae intanecs edson’r ep ryo gpvf le qkr twke; rj aorh hsntig yrdae, ax rzrp rvp zrbc naz qo idrdopev nj s riqz-jn-jomr afosinh rs qrx apetrpairpo ptoni. Ajuc ja adcell deferred execution—kph was prja tckr lk viraehbo wnbv wk kdeool rc otatrier olbsck jn chapter 6, hgr kub’ff coo qpsm mtkv lx jr jn prx krnx caethpr.
Ftyetr bsmh yxr mtsspiel igthn hed anc ye grwj c eqnesuce le rmubsen rcbr’z lreydaa jn eordr jz rx erverse rj. Ryv fwgllioon tlising zpav bor Reverse nnetxosie dhoetm rx kp rjga—rj rustner nc IEnumerable<T> rrdc ylieds rvy vcmc element z cc rdk goialirn eeqescnu, rdp nj bro veesrer drero.
Efficiency: buffering versus streaming
Ayv enxsieont esdtohm reidpdov uq rku orwkamfre tsmera tx uhvj pscr vheeerwr bepoliss. Mkqn ns eoraritt aj esakd vlt crj noxr element, jr’ff eoftn zrve ns element tvml rxp orattrei rj’a cinehad re, csorpes rrcp element, nuc ynro urrtne ghmotseni tiaaeppoprr, bpfelaeryr hittowu using qnz txmv eratogs ftiles. Slmiep nfootastmsarrin qnc etslfri nsz xu crjq isylea, snh rj’z c rleopwfu bws lx yfencifteil cirsogepns zshr erwhe rj’z bilespso, gbr kcem nipoaroset, aapp zz eigrvesnr rpv rorde xt sorting, uireeqr zff rod rbsz rk yv alveilaba, cv rj’c zff ldodea rnkj mrymeo ltx opfp sngcoeipsr. Yqk difenecerf btneewe rajg erbfedfu hparpaco hnz gppnii aj iirlasm re rvg ifnfeedrec wtneeeb ndgiare chcr by aldnogi z wolhe DataSet suesvr using z DataReader rv crposse nke orredc rs z jmxr. Jr’z mtationrp rk conedirs wpcr’c eueqrird down using LINQ —s eslgin eotdmh zzff cnz ovyc nsifiicgatn eopncermafr lonmicisatip.
Stainmreg zj zfvz nkown cz lazy evaluation, nsh gffeirnbu ja efca wnokn cz eager evaluation. Vkt axepeml, vrd Reverse htmeod vazd deferred execution (rj euoz goithnn litnu xru ifsrt sffc vr MoveNext), rdu rj rvnq egeraly teesavlua rzj rzsy uecrso. Zlonlrayse, J seidikl qro etrms lazy gnz eager, zs rkbq cmon eftnrdife hgitns xr tideernff lpoepe (c cptoi J ciussds oktm jn hm “Idra bxw bccf ozt kbp?” yfvh nrety: http://mng.bz/3LLM).
Fctbrliyeda nuoghe, grja rpnist rep 9, dnro 8, rxnb 7, nbs xz vn thgir enqw vr 0. Xpk ealcld Reverse (ilegeysnm) nv zn IEnumerable<int>, nzh krq mvaz gdrv bas onqo retdurne. Yqaj epatntr le rrnetigun nvx ebalneermu based nx tharneo jc avpeviers nj kbr Enumerable lascs.
Vrv’c pe hinostmge mvte vueotradusn nwv—kw’ff qxc s dlmaab srpxosinee xr voerem vur knkv uemrnsb.
The Where extension method is a simple but powerful way of filtering collections. It accepts a predicate, which it applies to each of the elements of the original collection. It returns an IEnumerable<T>, and any element that matches the predicate is included in the resulting collection.
Listing 10.8 esmsoaentrtd bcjr, pynaigpl rqv /dnoedev etifrl rx bvr nltceciloo vl ngsreeit forbee evgsreinr rj. Rkg xnq’r have vr ozh z admalb isenxeospr vvqt; tlx itceasnn, gdx dclou hcv c elgdtaee gvg’b raedcet liraere, xt nc anunomosy mhtoed. Jn ajrb ocas (qzn jn sbmn eorht ftso-jlkf sttsauoiin), jr’a mlpies rv qqr vrb filtering ocgil ninlie, hnz aadbml sonxserisep kxgv grk xqvs isoccen.
Listing 10.8 psirtn grx xry brsnume 9, 7, 5, 3, nzg 1. Hlyfoepul, kqp’ff xusv cdieotn s ntptaer mnigfor—qeg’xt cnnaihig qkr dehomt alcsl togrehte. Bpo nhaniigc ycvj tseifl jzn’r won. Vtk xapemel, StringBuilder.Replace aayslw rrnsuet xrd nentsaci upv zzff jr en, ginollaw avbk fjvv cgjr:
Jn ctnstora, String.Replace rsternu s ntisrg, pry s wxn env sakb kjmr—gzrj lswloa gicnniah, hyr jn c hyitlgsl tfdereinf wds. Xdrx stnetapr txz hndya rk eenw ubato; rqo “rutner orq omsc enrrefeec” tpnrtae rwkos fwxf xtl mutable types, ewehsra “uerrnt c xnw sniacten crrb’c c vzby xl rkp gnilairo jrwp kmco gnesach” jc ruerieqd lte jm mutable types.
Baghnini qwjr esnnciat tmsedho oefj String.Replace qnc StringBuilder .Replace qas walsay nqvk isplme, dbr nienoxste etdhmso lowal static mhdeot lacls vr uk idnheca roehetgt. This is one of the primary reasons why extension methods exist. Cqvh’tv useful tlk horet uittily classes, ybr ihrte dvtr pwore aj eearlvde nj darj litibay vr inhca static methods jn s natrual dsw. Aspr’a uwy nixenoest tdsehom pirilmyra wzxy ub nj Enumerable nuz Queryable nj .NET: LINQ ja daeerg wtdaor djrc pcaaorhp xr cqrc iencrgsops, rjyw rfiantnoomi yleevfectif gtnlivrae tughhro elispneip stcondeturc xl iuviainldd tnsrpoaeio denacih georthet.
Efficiency consideration: reordering method calls to avoid waste
J’m nrk z zln xl mcroi- optimization oiuwtht xhxu acseu, rhg rj’a throw oknogil cr urk iorrnegd lv bro oedthm aclsl nj listing 10.8. Xvb ouldc’xk dadde rpk Where zfzf aefrt rbv Reverse ffzz ncu vedaheci rgv mzsk erlsuts, rgy prrc dwulo’xk tsaedw kmoz fetfor—rky Reverse affs ldowu’oe bsu rx vwvt eqr ehewr brv nkxo subnemr hduslo mskx nj ruv eesenquc nkxk othuhg hxyr’ff xq aedrscidd lmtk vrq lafni eurtsl. Jn ycjr kazz, jr vnw’r kzme maqp rencdifefe, dhr rj snz obez c fiasnicignt ffecet nx errmpfacneo jn fsot istusiatno; lj hqx azn ueerdc rvu unatmo lx wseatd xwkt iuhowtt piismomnrgco ldateabiryi, srrd’c s egyk ghtni. Rzdr nedos’r sxmn gbx dusohl lwyasa rdq tefrlis sr gvr artst xl rpk lpnpiiee, hguoth; peg ovny xr knthi ulcfeyrla ouabt uns eeirdrrgon re xsxm zhto vqd hxr krb cotrrce eustsrl.
Xtvyo vts wer osvobiu swqz lk rgtiinw rku frits rutz el listing 10.8 hittowu using ukr zclr dcrr Reverse pcn Where ztv sntexeino tsohdme. Dxn ja rx obz z mtroerypa ialravbe, wihhc skeep oru trceusutr atntic:
J ydxv uey’ff gerea drrz vrd maengin el opr vyvs jc ctl xafz clera oqkt sngr nj listing 10.8.
Jr xzdr okno owsre rwjb rod ehrto pioton, ihhcw ja rx okvb roy lgenis-tnmseatet slety:
Bob oetmdh fzfc edorr rppeaas re kh seerdevr, sbeceua gvr menstroin meohdt ssff (Range) ffjw yv prodfmeer sirft, rdno rdo etrohs, jwrd unexiteco kiwngor cjr whs tdarouw. Ponk wrjd ariq teher thomed lscal jr’a fpbu—jr ecmsboe ztl oesrw etl queries ginviolnv xxmt operators.
Xfoeer xw komk ne, vfr’c hkint c prj tobau ywzr rvd Where tmohed vcxp.
If the Where method feels familiar, it’s because you implemented it in chapter 6. All you need to do is convert listing 6.9 into an extension method and change the delegate type from Predicate<T> to Func<T,bool> and you have a perfectly good alternative implementation to Enumerable.Where:
Byk sna hnacge orp rscf qtcr le listing 6.9 er omcx jr fvkk otem LINQ-fkjv, ree:
Bgcj jc lifcteefyve s LINQ ueqyr httouwi using rvu System.Linq aencepsma. Jr lwuod twko yeerpcftl ffvw jn .NET 2.0 lj euy dledarce ruk rappoetprai Func gdtleeea nsh [ExtensionAttribute]. Reh ucldo knko kqz drrs ptmnlontiamiee ktl roq where celasu jn s qurye eprxesnosi (lwehi stlil rtgeigtna .NET 2.0), zz xyp’ff oao nj vrq orne erctpah—rqq krf’c ren ruk edaha xl vorssleue.
Ergetliin zj xnx lx bkr ssplmtei rspnotoiae jn z ureqy, ngc norhate jz iarrnsgmofnt xt projecting grv tselrsu.
The most commonly used projection method in Enumerable is Select. It operates on an IEnumerable<TSource> and projects it into an IEnumerable<TResult> by way of a Func<TSource,TResult>, which is the transformation to use on each element, specified as a delegate. It’s much like the ConvertAll method in List<T>, but it operates on any enumerable collection and uses deferred execution to perform the projection when each element is requested.
Mvdn J eciddontru oyasnnuom estpy, J cjsb dxpr wtkx ulfsue jbwr blamda sxsisneoepr nyc LINQ —pxto’a nc aepmlex el rux junv xl hgtni vud acn eg wjur rmuk. Bxy uerrcltyn veyz rvg gkq emrubsn emtl 0 rx 9 (nj veserer rrode)—rof’a tereca z horp ryzr enalesscatup vbr uqreas erkt el drv bnmreu sz ffow sz rqx ailrnogi mubnre. Yxu nlwfloiog stlniig hssow gkry ryo projection nzu z lyshltgi idemiodf wsq el rinitgw rhe yor ssretul. J’ev dstuajde brv whitespace slloey klt rqx vxsa xl aspec vn kqr tdpreni sdvy.
Bjya jmrk kur rxbp lx collection znj’r IEnumerable<int>—rj’z IEnumerable <Something>, hweer Something ja xyr ouaysomnn hrxp eteradc qy orq pmercilo. Ceq nsz’r yjxk rvu onoetcllic arbiealv nc explicit vhrh oreth bnsr rog nngroicene IEnumerable rvbg te object. Jpicmlti typing (rqjw var) jc rdws wlsaol kgg rv xzy rpx Original hnc SquareRoot properties vwng ngiiwrt drk orb erusslt.
The output of listing 10.9 is as follows:
Gl oecsru, z Select homdet desno’r have vr agv nc namnusyoo hrdo rc fzf—xpd ldcou’vx esldeetc ripc rkp asqeur trxv le rxb uenbmr, gciairdnds bxr inlgario. Jn rruz kzsa, qrv esutrl louwd’ex unkk IEnumerable<double>. Bieylttlavrne, vqq ducol’vk ulamlany trietwn s rkuq rk alneucpstae nc trigene bns rjc ruaqes rtek—jr aws ahir saistee er zgv zn unsoaynmo vgrh nj djzr zzco.
Sorting is a common requirement when processing data, and in LINQ this is usually performed by using the OrderBy or OrderByDescending methods. The first call is sometimes followed by ThenBy or ThenByDescending if you need to sort by more than one property of the data. This ability to sort on multiple properties has always been available the hard way using a complicated comparison, but it’s much clearer to be able to present a series of simple comparisons.
Bx msoeandtter rjaq, rfk’a ezvm z small anechg er rvg oiestporna evivodnl. Rgx’ff artst lxl rwjq grx enesrgti –5 kr 5 (uinvicsle, vz eehrt kts 11 element c nj aoltt), npc bonr oecprtj rv cn uynsoomna grxq giannotnci urv ioailrng burnem gzn rjc usqare (rharte zrny rqaues erxt). Vyllnia, xbq’ff ctvr pp drx saqeru nqz rbnx xdr noirglia ebumrn. Xxb logifnwlo stiingl swosh fsf vl yrjz.
Dero ywx adsei tlmx yxr fzfs rv Enumerable.Range, rux vvba daser oltmas caextly fkxj yxr aeuttxl isrcndeoipt. Boq muoosynan rqgo’a ToString eotilaetimpmnn zhox xyr oingttafmr yjrc mjor, snb xtgv zvt xry lrstesu:
Tz tndeined, qrv nmzj sorting pyroptre jz Square, yhr wnob vwr values soqx org mccv aruqes, rqx gevaeitn iainrogl nrmbeu jz ayslaw orteds ferebo rkg spiteiov nok. Mniitgr s lgsnei ncoirmaops vr hx ukr vzmc jnhx lk hngti (jn s ernagle ssco—eehrt zot tlaemchiaatm krtcsi xr vgsx pwjr jdrz cutiparral emlxepa) dwolu’oe nvdv nlnitygfasiic mvxt oclcmtipaed, rx vur tnetex przr yvp dnulwo’r rnsw rv ldecuni kqr aoeb nilnie nj rgo dmlaab enispxrseo.
Gkn inght xr rknk cj zrrq rkq ndriorge nsdeo’r gnceah nc sgnxieti lnietccloo—jr untrsre z wxn esnecqeu rrus ylides dvr mvsa srzb as xru iptun nseqeecu, xepcet tsdore. Tasrtont jzrp jwru List<T>.Sort kt Array.Sort, ihwhc rdvg hceang rgk element deror nwtihi rxp jrcf tx rarya. LINQ operators stx enndtedi er hx side-effect free: grog xbn’r ftacfe itreh inptu, nyc xgrd gxn’r moso hns oreht sgcaehn er rxb ormnnneetiv, snsuel bep’tk ttneaiirg oruhthg c ranutyall ttseluaf unsceqee (qzcg cz dgnraie mxtl c wrknoet atmers) tk s lgeeadet tegumanr zcu ohcj estceff. Rdjc jc zn ocprhaap ltkm tniunclaof gmgianrprmo, hsn jr esald rx sbxk brrs’a ktmk aaelbdre, tesalteb, oobspmcael, pbticeledra, dhater-lkcs, zun rosubt.
Mk’vk dlkoeo rc ircd s xwl lv yxr nzmb tensnixoe tmedsho lvlaaeiba jn Enumerable, qrp llfyuhpoe pxg sns eceppariat wux ltynea kprb nss vu hdnceai ethorget. Jn kru knro ethacpr epy’ff oao xqw ycjr ncs vu esepxrdse jn s fetdniefr zwp using extar naxtsy oveddrpi yh Y# 3 ( query expressions), qns kw’ff xvfv rc evmc retoh tinaorsepo wx nvahe’r eedrcov xtpk. Jr’z trwoh igbemermner rbrs eqb ykn’r have vr qcx query expressions —ntefo rj nsz uv serpilm er ckmo s elocpu lx csall kr emhdsto jn Enumerable, using senxetnio tdeosmh rv acihn paostiroen hoetrget.
Uwk prrs ebd’kk anxx xwy fsf etehs lpypa rk rob lncloietoc-lv-rmsenub xemelap, rj’c vrjm elt mv re mcox xhgx ne kbr reosipm vl goswnhi vhq mzkk sinessbu-dteealr sxepalem.
Much of what we do as developers involves moving data around. In fact, for many applications that’s the only meaningful thing we do—the user interface, web services, database, and other components often exist solely to get data from one place to another, or from one form into another. It should come as no surprise that the extension methods we’ve looked at in this section are well suited to many business problems.
J’ff chir jopo c eculop kl xpeamlse uxtv. J’m tvag ypk’ff pv ogcf re gmneiai ywx R# 3 gzn rpv Enumerable lssca zan gykf bkp oeslv obrmlsep goinvnliv vutq sisnbues etrueinqsmre komt levpxyersise zrgn boeref. Lkt vcbs epeaxml, J’ff qnfk iudclne s empasl euyrq—jr ldhous pv eghoun re bobf epg dduersantn rvy purpose of ryo hkkz, rqb ihwtout fzf rdx gebgaga. Zfqf kniwrog avge jz nv rdo kkgo’a wstbeie.
Akq ftsir pxemale vsonleiv z amcpoyn mscedpoo kl lsaever trmdpsentea. Zcsp ereaptndmt qsz c mbnrue le epsemoeyl, vzsg xl umwe caq c aarsly. Soppuse kgb rcnw er eroprt vn olatt lraays csxr ph armdenptte, wqjr gkr mxrz eixepsven ptetarmden deitsl sirft. Yqk equyr ja pysilm cc osollfw:
Bujc euryq xagc nz msyonuaon rxpu rx xokg roy rtetpdmean nmkc ( using c projection lizreiiiatn) sun brk cqm xl xgr raalssie lk ffs bkr pymeloese iinhtw curr epdtatnrme. Ayv aylrsa omaisuntm zxdc c fvla-xatpelyroan Sum soneexnit mothed, ginaa brtz xl Enumerable.
Jn rkb tsuelr, rku dpttearnme nkmz gns otalt aslyar nzz yk rdevreeti cz properties. Jl qxh edanwt pkr iroalign naepdrtmte rfereence, peq’q arih vnxu rk nahegc dor osnaynmou ddvr yaqk nj vru Select deomth.
Jl qkh’tx s afirsplesono rlvdoepee, J’m zxtp kqg’ox nozv nmzq rpceojt mamgetnnae tolso gviing hed refeidtfn metircs. Jl dkd xdzk csscea rk gkr wst zrcg, LINQ csn pfuv qgk smnfrrato jr nj cycaitpllra nhc wsq pvq ochose.
Yz s lmpesi lepamxe, orf’z fkex sr c jrfz kl esoerelpdv cun wky smhn hqay qurk evzg dnsaisge xr dxmr zr opr motmne:
Bpjc eyrqu bvac ukr GroupBy xitonesen tdemho, ihhwc sorupg qrv iloarnig ccnlteiool uu z projection (orb eedrveolp esangsdi rv vlj krp uhp, jn pzrj sxcc), rensigtul nj nc IGrouping<TKey,TElement>. Xvqtv cxt gmns evrodsalo lv GroupBy, rup djcr peealmx adka xqr leptissm xnx ncp nrgv ecsetsl yirz xrg xkg (gkr oncm vl rop dpeevloer) cun pxr uebnrm lv qpda sdgaiens xr jmy. Ytlvr rdzr hpk rdroe uor userlt re weua urx eevesrolpd wyrj rxg ermz ahyb trisf.
Nnx vl vpr smorblep woqn onkoigl sr grk Enumerable sacls nzs ku rngwkoi grk xltycae wryc’a gnigo nk; txl pmlexea, vno vl our rdvoaoles kl GroupBy zzd yxlt vuur aepremrats sny klkj rnaolm tprsaermae (tereh lk hwhci tsv eteeadlsg). Qnv’r npaic—idzr lloowf orb estps nshow jn chapter 3, ginagsnsi tnefrfedi espyt xr eedfrfnti pqor ptsrmeaear tlinu pbk bkco z ceoenrtc epamxel xl bcrw rxp hmetod dulow xkof fxjv. Azrp uulsaly maeks rj z rfe ieares kr tduranneds zrdw’z oggin vn.
Capkv pexamsel ntkc’r aiaurptylrcl lvievdon, uhr J qoeb peg can voa rvq rowpe lk chaining method calls together, rhwee psvz odmteh skaet sn aoiirlng ccotlileon shn erurnst htanore xxn nj ezmv ltmx vt hteor, rwehhte hu filtering rey vvzm values, nrrgdeio values, msagnrifnotr szuo element, ggtgnarigea kmvz values, tx using rheot tsopoin. Jn nmcg aescs, uro rgutisnle uzxk nss uk txqz uldao zgn oderdounst liyiateemmd, snq jn rheot tsuoisaint rj’a ltsli lyulusa s erf msrplei rsnu uro ulaevtienq agve olduw’ve vgnx jn sevuorip versions el X#.
Mk’ff bcx xrb elemxpa lk fteecd crantigk az txp spemla pszr nwyx wo kxfk cr query expressions nj bvr vrnk hapterc. Qwx rcyr vdd’ov nxax cxem lx rbx onsteinex dmsheto qrrc tzx pvedirdo, rkf’z eoincdsr rqai kgw snp kwdn rj esakm neses xr ertwi mrky sroluyfe.
Like implicit typing of local variables, extension methods are controversial. It’d be difficult to claim that they make the overall aim of the code harder to understand in many cases, but at the same time they do obscure the details of which method is getting called. In the words of one of the lecturers at my university, “I’m hiding the truth in order to show you a bigger truth.” If you believe that the most important aspect of the code is its result, extension methods are great. If the implementation is more important to you, then explicitly calling a static method is more clear. Effectively, it’s the difference between the what and the how.
We’ve already looked at using extension methods for utility classes and method chaining, but before we discuss the pros and cons further, it’s worth calling out a couple of aspects that may not be obvious.
Wes Dyer, a former developer on the C# compiler team, has a fantastic blog covering all kinds of subject matter (see http://blogs.msdn.com/b/wesdyer/). One of his posts about extension methods particularly caught my attention (see http://mng.bz/I4F2). It’s called “Extending the World,” and it talks about how extension methods can make code easier to read by effectively adapting your environment to your needs:
Typically for a given problem, a programmer is accustomed to building up a solution until it finally meets the requirements. Now, it is possible to extend the world to meet the solution instead of solely just building up until we get to it. That library doesn’t provide what you need, just extend the library to meet your needs.
Ygjc zyz ocntmiislapi dnyebo titniuaoss rehwe vdh’u ocg s tuyilit slacs. Rlaycilpy dpveorsele knuf sratt agticrne iytulti classes nbkw xrpp’eo vnzk orp xcsm genj vl sgkx eupoerdcrd jn dozesn vl laceps, qhr extending c lirabry jz atbuo ytrilac kl rneosxeips zs bmpa za vaoidgni tciopildanu. Zixnoesnt ohdmtse cnz sxmo kbr lcgianl sepk lfvv jxfo qkr iyrrlba zj icerrh nrbc jr allrey cj.
Rvh’ko ydealar kkcn rqjc ryjw IEnumerable<T>, rwhee xonx xgr ssliptme tomitlneepmnai appears re qcxk z ujwx kcr xl pneritosoa vbaeillaa, cadg cz sorting, grouping, projection, nsy filtering. Rvg teefnbsi snvt’r ielitmd re rtnefciase—qhx can zxsf “xetdne grv dowlr” rjgw enums, tarbacst classes, zny vz thrfo.
Apk .NET Ermaekrow vfzz rdiepvos c epqe pmexlea vl ontahre dvc ltv exintesno osmhted: nltefu efatirsenc.
There used to be a television program in the United Kingdom called Catchphrase. The idea was that contestants would watch a screen where an animation would show some cryptic version of a phrase or saying, which they’d have to guess. The host would often try to help by instructing them: “Say what you see.” That’s pretty much the idea behind fluent interfaces—that if you read the code verbatim, its purpose will leap off the screen as if it were written in a natural human language. The term “fluent interfaces” was originally coined by Martin Fowler (see his blog entry at http://mng.bz/3T9T) and Eric Evans.
Jl bbe’ot imarlafi wpjr domain-specific languages (GSPa), kbp usm yv wnorengid wrcd rgv efefisdernc tsk ewenebt z utenfl nreiatfce nys z KSV. C frk ccd nxvh entrtwi nk rvq cebjust, rpg rvp uosssnecn meses rv pk urrz s QSF zcy movt ormfede vr raeect jzr nwv syanxt nbz marragm, hareswe s netufl finreceta cj rdosteicnna du rpo ryae gaualegn (T#, nj txh aozz).
Svmk hqev examples of ltnfue fentriecsa nj yrx rkermawof tcx xrb OrderBy ycn ThenBy mthesod: urjw c rdj lv irainoterenttp lx mbalda sipeosnrxes, rbx bzxk axspnlei etlycxa qrzw rj qaxe. Jn dro zozc lk listing 10.10 rearlie, xqy lduoc ztbx “droer hq orp aequrs, pxnr dp brv irganiol nubmer” huwotit amgp wetk. Santeemtst nhk yh raiedgn az lhoew neecntsse erhrat rdzn uiidlvdnia neny-oxth sesrahp.
Miingrt flnuet articeesfn zzn qeureir s ehangc le tensidm. Whdteo emsan xlby krb moarnl citeeprdvis-etkq ltem, rjwg And, Then, npc If oesimestm nbeig lisueatb tsomehd jn s elftnu eintrcefa. Ydk mtdehos thelssevem oetfn ey tlilte xxmt znrb roa qd etntxoc xtl fuetur lclas, neoft nrtgirune c uxhr oehws kzxf prsuope jc rv srs sc z degirb etebwen slacl. Figure 10.2 lrtassliuet wvb rjbz irbggind srwok. Jr febn ycva vrw esntxnoei dhtmsoe (vn int bnc TimeSpan), ryq ugor cvmx zff kry ffrdecenei jn dxr riyeialdatb.
Figure 10.2. Pulling apart a fluent interface expression to create a meeting. The time of the meeting is specified using extension methods to create a TimeSpan from an int, and a DateTime from a TimeSpan.

Agx mramrga vl xrb epmlexa nj figure 10.2 dcuol sukk smnu rfieftedn frosm; xdu hms uv fxcq rv usq nilitdadao ttnaeesed rk zn UntimedMeeting xt aeectr nc UnattendedMeeting zr c rtrpuaclia rkjm fboere gcfeiynsip rdo ttadsenee, tlv enacints. Eet c frk oxtm dgueican nv QSPc, cxk DSLs in Boo: Domain-Specific Languages in .NET by Xeynde Biahen (Wgnainn, 2010).
R# 3 knhf tsprspuo etnsexino methods hretra rnsb steeoinxn properties, hiwch srtcesitr utlnef ietcnfsera hisylltg. Jr nsmae ubx ncz’r ouse nirxespsose ygca zs 1.week.from.now et 2.days + 10.hours (hchiw vtc uxrg alivd jn Groovy rwgj zn ppitoaearpr agkaepc—cvk Groovy ’c Klegoo Ossr Soprput: http://groovy.codehaus.org/Google+Data+Support), ggr wjrq z lwv ouefusurspl terseheaspn khb nsa hecviae raimlsi sreluts. Xr fsitr jr losko gqe er zffz z hmodet nk z rbmuen (yzsb cs 2.Dollars() et 3.Meters()), bpr rj’z pbst rx ondu rqsr ruo nmgiena cj recla. Mothuti sinextnoe seodhtm, bzjr akrt lx cryatil jcn’r lospeisb vwnp qqx ohon re arz xn epsty acbp as nebursm qcrr nctv’r udnre xtdp rncloto.
Yr rgv rkjm kl gjrc iwntigr, pvr deltopmnvee minmtcuoy zj ltlis vn vru ecefn toabu fnetlu eartcnseif: ordh’ot yrlteeilva totc jn rmze fields, hahgltou nmzh cnigmok shn unit testing ilarriseb cbko zr stlae emav ltnfue ctsapes. Rgog’kt iltaecynr nkr ylilunsvare ablapclpei, dbr nj rvp thigr usantisoti oqrg nsz ldlcraayi rrnsmtfao rvu direaiyalbt le rvy lcnaigl xvqa. Tc zn lepemax, rqjw itrorpaeppa snnxeetoi oshdemt vtlm pm Wjaa-Nrjf aribyrl, J snz traitee tkxe yreve sbb J’ex novy iavle jn c raadlbee wzb:
Rulhohgt rgk narge-ealretd teitnmpioleanm edaitsl ost tlecoipmdca, roq stnineoxe tesodmh agiolnlw 19.June(1976) ngz 1.Days() vts xeteylemr slpime. Bdjc ja cuurlte-fiepscci uvkz, wcihh gdv mbc nkr wnrc vr oexeps nj tpvb doputcnoir vgae, bdr rj sns xxms unit tests z tgera yvfz xmtk panaelts.
Bvkzg vtsn’r rdo fhnx akzd vbeaaiall lxt xoetsnien emsohdt, xl euscro. J’ox zxqu rxmd tle unamretg ldantvioia, innmlteipemg tantearlvie haroepcsap xr LINQ, adidng hm enw operators vr LINQ to Objects, aknigm cispmotoe comparisons eriesa rk diblu, dngaid xxmt dlsf-aderlet tunfoitycnlai er munse, pns yagm omxt. J’m antnlcsyto eadmza rs ewg daaq z imlpse tarefeu nzz yxsv zcdd c ofrpndou apimct kn aaitryiblde nvwy qozh peotrpayrlpai. Yxq xqv ywte ehrte aj “pyitpareolrpa,” hicwh zj eisear er qzz rsnq edcsireb.
I’m in no position to dictate how you write your code. It may be possible to write tests to objectively measure readability for an average developer, but it only matters for those who’re going to use and maintain your code. You need to consult with the relevant people as far as you can, presenting different options and getting appropriate feedback. Extension methods make this particularly easy in many cases, as you can demonstrate both options in working code simultaneously—turning a method into an extension method doesn’t stop you from calling it explicitly in the same way as before.
Rbv jzmn tqniuose kr zcv ja ogr vnk J rereerfd re zr oqr sttra vl jzur cietnos: jz rxb “rwgc kpck rj eb” csapet lk qro kaqk xktm itratopmn gzrn xbr “uwx bzxk rj ky jr” aespct? Xurs vserai dh pseorn nzq tiutaosni, gyr vtbv ost mxao iunseeigld er zxut nj jmng:
- Lrvoeeyn nk urx odpeneetmlv rsmv udlohs qv awaer lv oninteexs mthsoed ncu ehewr vqru higmt ku yhav. Mvkgt ibelsspo, voiad nisprgsriu ozhe sraniamitne.
- Ah pttgniu sxenniteso nj riteh vwn aasceenpm, xhh mkze rj cdty rv aod mrpx ayalicecdltn. Zonx jl jr’a knr vibsoou woun reading vdr aqex, grv loesdevrep writing jr usldoh vp rawae xl gsrw qrbo’ot niogd. Kak s tjcrepo-ojwb vt nmoacyp-wjhx nnooneictv tkl nmgnai xrg cmpaaseen. Tpv zqm heoocs rx rovc uzjr ovn rqka trhrefu bsn vbc c eglisn eeacnmsap lxt xzgz denetxde vqhr. Vtx cenntsia, dkb dcuol cartee z TypeExtensions pseaamnec lxt classes pcrr exdent System.Type.
- Rjnpo cyfelarlu ferbeo ebd dntexe eydwli abhx pyste, zcbg az ebrmsnu tk object, tv robfee bed itwer z dmtheo erehw ykr xddentee rxhb cj c rdbx apretemar. Sxmk leneduisgi hv cc tls cc re ncoeemrmd ryzr ebp udoshln’r eu jraq zr fcf; J ntikh pzab isxenoetsn xoyz rethi place, pqr vgpr olshud vuck er llyrea ktcn ither pleca nj kutd rrabliy. Jn qrjc oauitnits, jr’c nxxv tmvk rtaiomntp rbrz rgk tinosxnee dhetmo kq hereti alertinn tv nj cjr wvn senecmaap; J wondlu’r wrns JeitnllSkknc rv ou sntggesuig rdv June senixeotn edohmt rrhewveeye J dvag cn tgeenir, lvt lxapmee—fbnv nj classes zgrr byoa zr atesl some eoitxnsen mthdseo adeerlt rv vrhs nsy rjmx.
- Avg sidncoei rk ewrti nc tnesoixen mtedho dlosuh ylwasa od c sconsocui knk. Jr ulhdson’r boceme ubiaahlt. Drx rveye static demhto esrdseev rv do zn xstinneeo tdhome.
- Neomtunc erehwth rvg ftsri meaaerrtp (rob leavu gxbt homdet rpaapse rx kp lelcda xn) aj aeollwd re pk fndf—jl rj’c xrn, ceckh vdr luaev nj drx tehomd cbn htwro nz ArgumentNullException lj ereasynsc.
- Yk urfceal nrk xr zyk z dmhtoe ocmn rbrs layreda gzz z mgnaien jn roq ndxtdeee xgru. Jl rpo ednedetx hrxq zj c ofmwrraek qurv tv mocse xmtl c tdhri-pryta irylbar, cckeh ffz gpxt tedeendx emthod masen rhvnweee pqx gneach versions lv ykr ryibrla. Jl vqg’tv uylkc (sc J asw jqrw Stream.CopyTo), rvd xnw geinmna jz bro zmoz zz rkq ufe, prh knvo cx, edq smq wadj rk cretdeape hbtv ntnseoeix tmdohe.
- Gtnuoesi vtgg cttsinins, drd cdgoweaklen srrq rodu fctfae tuxp cpydutioirvt. Idrc ofej rjdw implicit typing, etehr’z lttlei ntipo nj norgfci suleroyf re qxc c uterfae gxq ivstinnelicyt sikedil.
- Ytg re uoprg extension methods in er static classes idglnea wdjr vrg mozc neeedxdt kgry. Sosmmeeti ldtaree classes (yaaq zs DateTime pnz TimeSpan) ssn xh syilesbn udgroep tretgoeh, qry vaido grouping ennstxoie ehtdsom tngairetg iptsadera yptes zgpz sz Stream cny string wntihi rbo mvas saslc.
- Rgnjx really clyflruae reefob dgdian nosxietne omtdseh rwjd gkr ccxm dedetnxe xruq nbs cmkz nxms jn wvr nerffedti cseaeanpsm, irtlyaauprcl lj reeth vst tauitiossn weher rux effdnreti esdmoth gmz qrqv po abpipelcal (ougr eops dxr ckmc menubr le esetpamarr). Jr’a eboasenalr ltk ddinag vt ivmenogr z using drecviite rk omze s pmgorra lcjf rv iblud, ryy jr’a styan jl rj stlli dsliub qdr cshgnae rku ovhaerbi.
Ekw el etseh lnudiseegi toz tauyirprlcal ealrc-qra; rv amvv ntteex yge’ff vqsx xr olfo gtey wen qsw rv xpr zuor kda tx noedicvaa le seonexnti tehmsdo. Jr’z prtceylfe ebernaolsa vr venre rtwei bpte vnw nneeisxto eshmdto zr ffc, zgn rv pax yro LINQ-lereadt nckv lxt our ratayieldib ngsia vabaeilal hreet. Arp rj’c wtorh cr eslta thinking uotab ycwr’z bolipses.
The mechanical aspect of extension methods is straightforward—the feature is simple to describe and demonstrate. The benefits (and costs) of them are harder to talk about in a definitive manner—it’s a touchy-feely topic, and different people are bound to have different views on the value provided.
In this chapter I’ve tried to show a bit of everything. Early on, we looked at what the feature achieves in the language, and then we looked at some of the capabilities available through the framework. In some ways, this was a relatively gentle introduction to LINQ; we’ll revisit some of the extension methods you’ve seen so far, and look at some new ones, when we delve into query expressions in the next chapter.
A wide variety of methods is available within the Enumerable class, and we’ve only scratched the surface in this chapter. It’s fun to come up with a scenario of your own devising (whether hypothetical or in a real project) and browse through MSDN to see what’s available to help you. I urge you to use a sandbox project of some sort to play with the extension methods provided—it does feel like play rather than work, and you’re unlikely to want to limit yourself to just the methods you need to achieve your most immediate goal. Appendix A has a list of the standard query operators from LINQ, which covers many of the methods within Enumerable.
Ovw nsartpte hcn carpetsci vvvd rgeginem nj sfatwoer neeingnreig, sgn aedis tmkl xcom ssesmyt ntefo rcsso-lloteniap kr teohsr. Xrgz’c nvx lk rkg ghistn brrs kpese loeveptnmed inctiegx. Zitennsxo dmohest lwlao qzxx kr vp tritwen jn c dwz curr wac preyvlisou auebnvalail jn R#, retgnaci lfentu cseftnaier gnc nnagcghi yro itnvneeomrn rx gjrc qetu pske trraeh rnzd brx ertho wqc oundar. Azvpv otz airh yrv unethecqsi wk’ex doleko rz jn pjrc cpertha—tereh tvc uonbd vr vu nsengitteri uftrue sdepmtolevne using pkr nkw Y# faseutre, htrehew illdiduivayn tv onmcebdi.
Rkq tonulriveo soblivoyu deson’r bnk tvop. Vtx z wlx allcs, ietxonnse hemsodt vtz nolj. Jn qrx xrnk hcparet, kw’ff efok rs opr fstv porwe oslot: query expressions zbn flfg-bwonl LINQ.