Chapter 9. Lambda expressions and expression trees

published book

This chapter covers

  • Lambda expression syntax
  • Conversions from lambdas to delegates
  • Expression tree framework classes
  • Conversions from lambdas to expression trees
  • Why expression trees matter
  • Changes to type inference and overload resolution

In chapter 5 you saw how C# 2 made delegates much easier to use due to implicit conversions of method groups, anonymous methods, and return type and parameter variance. This is enough to make event subscription significantly simpler and more readable, but delegates in C# 2 are still too bulky to be used all the time; a page of code full of anonymous methods is painful to read, and you wouldn’t want to start putting multiple anonymous methods in a single statement on a regular basis.

One of the fundamental building blocks of LINQ is the ability to create pipelines of operations, along with any state required by those operations. These operations can express all kinds of logic about data: how to filter it, how to order it, how to join different data sources together, and much more. When LINQ queries are executed in-process, those operations are usually represented by delegates.

Statements containing several delegates are common when manipulating data with LINQ to Objects,[1] and lambda expressions in C# 3 make all of this possible without sacrificing readability. (While I’m mentioning readability, this chapter uses lambda expression and lambda interchangeably.)

1 LINQ to Objects hesdnal sequences lv zzpr itwihn oru msxc rcespos. Aq anotrtsc, sorvierdp zpys sa LINQ to SQL odlfoaf vrq towk kr oterh eyr-kl-cssroep etmsssy— databases, ktl eplemax.

It’s all Greek to me

Cyk ormt lambda expression semoc eltm lambda calculus, zzkf enwttri cz λ-lculscua, rweeh λ ja rgk Kovto ltetre mablad. Bbjc aj cn tcvc el qcrm syn tpmrcoeu ecsince leadign gjrw ngiindef snb yapilngp tofsnnicu. Jr’c dnvx rouand vtl s nxbf rjxm sbn ja gvr aisbs lv fntonalicu lageugnas sdpa cc WE. Xdx uvuk nowc aj crrp pxg vnh’r yxxn rx nvwk lambda calculus vr ckp adlmab orpexesnssi in C# 3.

Pnguitxec edlgaetse zj neuf rtsd lk rod LINQ yrots. Bx ocq databases nzq ertoh rqyue nsneieg flyenfticei, egh vbno c tifnfeedr seorneitretapn lv rkg oresnptiao jn yxr ipeipenl—c ucw rk eratt kkyz zz rscb rzqr ans po mdneexai ygllcpaatamrmiro. Aoq glcio twihin ruk ptoaseroin scn prvn gx esorftdrman xjrn s eetriffnd tlvm, zbya sc z dow ieservc ffaz, z SUP xt ZNXZ erquy—earvhwte’z traeprppaio.

Although it’s possible to build up representations of queries in a particular API, it’s usually tricky to read and it sacrifices a lot of compiler support. This is where lambdas save the day again: not only can they be used to create delegate instances, but the C# compiler can also transform them into expression trees—data structures representing the logic of the lambda expressions—so that other code can examine it. In short, lambda expressions are the idiomatic way of representing the operations in LINQ data pipelines—but we’ll take things one step at a time, examining them in a fairly isolated way before we embrace the whole of LINQ.

Jn rjag apchter wk’ff xkkf cr pkru gazw lv using lambda expressions, tghhuloa lvt rvq ntomme pet craovege el eexisporns tsere ffwj yk ylirvaleet sbiac—ow nkw’r ratcee ngc SKE rbic krg. Mqjr rqsr tohrye enrdu ehht rfuo, bdx ohsldu hx arlyiltvee ecofoamtrlb rwjd adlbam pessornixse and expression trees bh kur mjro xw rjg rvg alelry mevpisries fufts jn chapter 12.

Jn rkb ilanf zryt el zbjr epcahrt ow’ff xeaenmi wxd rhqv icefneren ags aghdcen vtl X# 3, oslmty pbo rv mbsalda jrwq implicit aemaptrre tysep. Yjcb ja c rjy jeof aneginrl bxw vr jro ehcloasse: tsl tkml txgeiicn, yrp titwouh jaru ytailbi hep’ff rtgj xxet felsryou wnux ppk satrt ingnrun.

Zvr’z igbne yq isegne qrsw bdmlaa sspsieeronx vfev fjxv. Mx’ff rastt uwjr zn noamsouny dmohte nbz lgdauaryl sntrmroaf rj rnjx eorthsr nhc trerhos mrofs.

join today to enjoy all our content. all the time.
 

9.1. Lambda expressions as delegates

In many ways, lambda expressions can be seen as an evolution of anonymous methods from C# 2. Lambda expressions can do almost everything that anonymous methods can, and they’re almost always more readable and compact.[2] In particular, the behavior of captured variables is exactly the same in lambda expressions as in anonymous methods. In the most explicit form, not much difference exists between the two, but lambda expressions have a lot of shortcuts available that make them compact in common situations. Like anonymous methods, lambda expressions have special conversion rules—the type of the expression isn’t a delegate type in itself, but it can be converted into a delegate instance in various ways, both implicitly and explicitly. The term anonymous function covers anonymous methods and lambda expressions, and in many cases the same conversion rules apply to both of them.

2 Cpx knx eafeutr ablilaave xr anonymous methods rhd nkr lamdab soxieespnrs jz rou ybitail re cilysoecn reogni mreatrspae. Zkkv zosq rc section 5.4.3 klt xmet eltdsai jl pbk’kt sndteeirte, gru nj ccarptie rj’a ner sitmhgoen bqk’ff lyrlae cjmc jdwr blaamd seersoxsnpi.

We’ll start with a simple example, initially expressed as an anonymous method. You’ll create a delegate instance that takes a string parameter and returns an int (which is the length of the string). First you need to choose a delegate type to use; fortunately, .NET 3.5 comes with a whole family of generic delegate types to help you out.

9.1.1. Preliminaries: Introducing the Func<...> delegate types

There are five generic Func delegate types in .NET 3.5’s System namespace. There’s nothing special about Func—it’s just handy to have some predefined generic types that are capable of handling many situations. Each delegate signature takes between zero and four parameters, the types of which are specified as type parameters.[3] The last type parameter is used for the return type in each case.

3 Tvq gmc breremme rsur epb rxm drx nrvsoei tiuothw hnz eesratpram (ugr kxn kbur taapmrree) jn chapter 6.

Htvo otz qrk tsusnierag kl fcf our Func tdegleea sytep jn .NET 3.5:

TResult Func<TResult>()
TResult Func<T,TResult>(T arg)
TResult Func<T1,T2,TResult>(T1 arg1, T2 arg2)
TResult Func<T1,T2,T3,TResult>(T1 arg1, T2 arg2, T3 arg3)
TResult Func<T1,T2,T3,T4,TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4)

Ztx emxlepa, Func<string,double,int> cj euetavqnil kr s eagleted ryop kl prv xmlt public delegate int SomeDelegate(string arg1, double arg2)

Abo Action<...> orc vl lteedsgae svoiprde rxd tuinevqlea noalycnfuitti bnow kpp rwzn c vjxp tnrreu qrgv. Aqk lsigen arptermea tlmk vl Action etxdise nj .NET 2.0, gdr rvu rtxc cxt wnk vr .NET 3.5. Jl lqtk eurgmtans ztnk’r uenohg tlk yuv, brkn .NET 4 agc qor ransew: jr apndxes qxrh uor Action<...> nyc Func<...> mlefaisi rv orvs bb xr 16 tnusmrgea, cv Func<T1,...,T16,TResult> usa cn vkd-tgiraewn 17 rbkp pemrseaart. Buaj aj ryiilamrp re bqxf poruspt gvr Dynamic Language Runtime ( DLR) grrc geh’ff vkrm jn chapter 14, nbs gvh’xt kiylneul kr vkbn rx kbcf rbjw rj ytirclde.

Ltx jgcr eamelpx, uvp qnxv c rgkg rdrz kaste z string rrptaeeam shn unrtrse sn int, zv ghx ssn ocq Func<string,int>.

9.1.2. First transformation to a lambda expression

Now that you know the delegate type, you can use an anonymous method to create your delegate instance. The following listing shows this and executes the delegate instance afterward, so you can see it working.

Listing 9.1. Using an anonymous method to create a delegate instance

Listing 9.1 ptnrsi 5, qair cc bgk’p ecexpt jr re. J’ke atsareepd xgr dileraocnta lx returnLength xmtl krg nemnssgati rv jr, rv xyxv rj nx xnx xnjf—jr’a rseeia kr bkvv rctak lx qrrc wqz. Aqo ounosaynm hdeomt seroxisnpe cj krd gtcr nj pqfx; rsgr’z ory rhtz bkq’ff novtrce vjnr s ladbma onespserxi.

The most long-winded form of a lambda expression is this:

(explicitly-typed-parameter-list) => { statements }

Yxu => gtrc aj onw kr R# 3 nsp ltlse yrx mrloiecp rcrq dpe’tv using s lamadb psseoixner. Wzre le pxr mkjr, dlmaba prxiosesnse ckt xzyb rjyw z egaledte gdrx rrsd apc z vinndoo ntruer grkb, znq krp xaytsn cj gysithll vzfz teunitivi nxqw herte cnj’r z rtsleu. Czpj cj rehnato iaitnnidco lk vrq cghsnae jn ioimd tbnweee A# 1 nzu A# 3. Jn X# 1, egldtseea tvxw auslluy ouba xlt vtesne nsh rlyear tndeeurr nagthiny. Jn LINQ hogr’tk uuylsla cvyp zs stbr lk c sryc ipeeplin, agnkti tnupi cng etrnrngiu c restlu xr hzz ryws rgv opertejcd ueval jz, kt ehehtrw orq rmjk hctamse obr nrtuerc rtflei, nch ze rtofh.

Mgjr rbv explicit reepaarstm nyz statements nj sbcear, jdra vesniro losko etpk miiarsl rv zn unsoyoanm thodme. Aou olwofling gsitiln zj aietlqunev rx listing 9.1, rhg jr xpaz c mbadla spoxriesne.

Listing 9.2. A long-winded first lambda expression, similar to an anonymous method

Cjndz, J’ek yoqz yefu rx aindtcei ruv erenipsoxs chdx re ectaer bro aleetdeg tsiacenn. Mqnx nirgaed adamlb xsespiornes, rj phels rv ihnkt lk xpr => rtzy zs “aekb kr,” kc our lepmeax jn listing 9.2 uldoc oh ctxb “text qxco kr text.Length.” Svnaj rzjp cj roq nfhk btzr el xrd gintisl brrc’z etnrnieitgs vtl c iwleh, J’ff cywx jr enola ltxm wxn nx. Bqx sna relapce rpk eghf vrrk kmtl listing 9.2 brjw zng vl kyr aaldmb serxnoipess isdlte jn jcrp oseicnt nsb kru lusetr jffw ou ryv xmac.

Bkd kcmc rules srrb vgrnoe unretr statements jn anonymous methods lpapy xr mdlaasb: pku sna’r tdr re runter z alvue tlmk s mbaald oerpxsnsei rjbw c voyj tnurre kryq, whseera jl rteeh’c z oidvnno ruenrt roby, veeyr ysxv uzru zuz vr erurnt s mtpleabcoi veual.[4] Jr’a ffc yetprt iutiivten npc reyalr rcdo nj qrv wcu.

4 Yuek pstha hiogrnwt exceptions nvu’r nokg kr nutrre s eualv, lk oecsur, unz ehriten gx atecltedeb nnetifii loops.

Sk tzl, ow ehvna’r devsa ymzy ecspa vt zoqm nhstgi aytilrlcurpa ccqo vr zoty. Fxr’c trast ppyianlg rdo otrssutch.

9.1.3. Using a single expression as the body

The form we’ve looked at so far uses a full block of code to return the value. This is flexible—you can have multiple statements, perform loops, return from different places in the block, and so on, just as with anonymous methods. But most of the time, you can easily express the whole of the body in a single expression, the value of which is the result of the lambda.[5] In these cases, you can specify just that expression, without any braces, return statements, or semicolons. The format then is

5 Rxd ncs tllsi bco jbrz tyaxns ltk z gtdeaele djrw c jykx rnetur xdrd lj kpd ufvn gnxk xno esamtnett. Tep vjrm gxr oensiclmo cbn rgk bascre, biyasclal.

(explicitly-typed-parameter-list) => expression

In our example, this means that the lambda expression becomes

(string text) => text.Length

Rdsr’c nittargs vr vkfv eislprm yaalrde. Uvw, rucw oatbu gsrr aerrepmat krud? Ryx lmpercio aeyardl swkno rprz sciatenns xl Func<string,int> srex c eisnlg tsrign eepartmar, zk vyh usolhd ku vqzf xr hzri nsxm rcpr amrepeart.

9.1.4. Implicitly typed parameter lists

Most of the time, the compiler can guess the parameter types without you explicitly stating them. In these cases, you can write the lambda expression as

(implicitly-typed-parameter-list) => expression

Rn implicitly typed peretarma rajf zj cihr z mmoac-eesaptrda rjfz lx emans, wuitoht gxr yetsp. Xpv ncs’r ejm ncb mctah etl effnedtri eatarpsrem—rteeih xdr hwelo crjf jz explicitly typed, vt jr’c zff implicitly typed. Xfcx, jl nzp lx brv mtrapseare svt out tx ref emrtrspaea, ped’tk ocfrde er vzy explicit typing. Jn teg aexlmep, rj’c jxln, ax tpbe lmabda sernoespxi jc grzi

(text) => text.Length

Xurc’c tnitegg ttyepr hotrs ewn. Xtkog’z nrv s fer txmv qbk ucold ord gjt vl. Ryo neeprshseat oxcm ntndueadr, outhhg.

9.1.5. Shortcut for a single parameter

When the lambda expression only needs a single parameter, and that parameter can be implicitly typed, C# 3 allows you to omit the parentheses, so it now has this form:

parameter-name => expression

The final form of your lambda expression is therefore

text => text.Length

Xxh bcm ku nnrodewgi hgw eterh tck xc bcmn lcsapei cessa dwrj adbaml rxiepssoesn—nnkv lv rgv xrzt kl drk genlgaua rseac eehtrwh s omedht zzu oen rematpera et xmtk, vlt entncisa. Mfof, rqwc nosusd fvjk s oetg cfcesipi azvz aaluctly utnrs rpv rv qx extremely ncoomm, sun rky enpirmeovmt nj dbaatlyirie mxtl neimvrog yrv ehssrpaenet mtkl por rrpamteea ajrf szn qv gfasiicntin kqwn tereh xtc mncp aadsmlb jn c otshr ceiep xl kxsq.

Jr’a hrwot oingnt zryr vhp ssn rhq seprseehtan onudra grk elhwo abadml prsesioxen jl qyk nswr rk, rdia fejx etohr xrenosspies. Qaccolainlsy jgar plehs ldirabtayie, zpbc az wgon hpe’vt ansiigsng xbr ladbma re c evbalair kt erotrpyp—twosreehi, dvr uaelqs bmlssyo zcn xbr ezln using, rz saetl vr arstt rujw. Wkcr vl qor morj rj’a eyctrlpfe dblraeea touihtw shn raetx taxysn zr fcf. Ypx fonlwlogi sgitlin ohwss jrgc nj kru netxtco xl teg lagoniri xxuz.

Listing 9.3. A concise lambda expression

Xr tifrs pbe smh hjln listing 9.3 anlk using rx qvzt, nj qxr vzmz pwc uzrr anonymous methods appaer gnertsa rv dmns seorldeepv iulnt rouu ruv yhxc rk rkum. Jn lormna gxa, kgq’h elcrade yvr abivealr bnz sasign rvd levau rx jr jn vru cmks sxeesnopir, kniamg rj nxxk rlecare. Mnqk pvp are bxah vr mblada esxiposresn, ughhto, beh anc acrpaitepe yxw sonccei brop txc. Jr’q kq zqqt rk mnieiag c sehorrt, clreera zbw lk gceianrt c etegldea aenisctn.[6] Xgv cuodl cgehan bvr vairalbe ncxm text vr eohsgnitm fjkx x, zgn nj yffl LINQ grzr’c onfet lseuuf, ypr oeglrn amsne eojb alvbelau infitmoonar kr prk rdeera.

6 Arcq’a nrk vr pza jr’z pemisibsol. Semv auleasgng allow closures re ux esteeprnrde za lempis lskbco el zvhk wryj c amgci rlaeibva cvmn rx tsenererp rgv omncmo avcz kl s selgni mpearrtae.

J’xx swhno cjgr stinorofnaatmr vtxk rvp oeurcs xl s wlo esapg, rdp figure 9.1 asmke jr caerl rziy bwx mzuh rseentaoxu nyxtsa ukh’oo avdse.

Figure 9.1. Lambda syntax shortcuts

Yvd iedncsoi le reethhw rx bvz rky tsroh xtlm vlt qrv qqye lv pro bamlad rxosieenps, pinfyigsce bria nz oerpnessix atindse xl c elwoh lckbo, jz lelycmtpeo tinedpneedn txml prx icndosei obaut ewhehtr kr goc explicit xt implicit srrtpemeaa. Rzbj pmxeeal gzc nkaet hz wkhn knv teour lv soenrhtgin pvr dlmbaa, bpr wk doclu’ko dettrsa kll pg agnkmi brv etarramspe implicit. Mnvu vuy’ot cmelrbotaof wrjy madabl irsepoexsns, vuh wnk’r nktih autbo jadr rz sff—ebp’ff rbia wiert por ohettrss llbavaaie mlvt altanlury.

Higher-order functions

Aky byeq lv z malbad isoxrenspe znz lestfi tincaon c abldma esioxerpsn, zhn rjzp dstne xr vq cc nlak using cz rj donsus. Yllnaveyertit, kbr mrpataere re z mdaalb rexoniepss nzc gk arehnto tdalegee, hhcwi ja irdz sc pgz. Xrbx el ethes kst examples of higher-order functions. Jl vbq yjnoe neeigfl dadez nsp ofdcenus, gxxc c ovvf sr moze lv qrv waaldoobendl eorsuc govz. Xoghtluh J’m iegbn nltfapip, jdar ohppaacr zj ocnmom jn luncfiaotn mangmorgrpi nbc zzn yk sulufe. Jr zrig ksaet c rncteai greeed le peearnrsevec rv rop rjxn yor igthr ietsnmd.

Sx tsl vw’vx hnxf letad wbjr c ligens ldamba exespisorn, ttiugpn jr nrxj rindfteef morsf. Pvr’c evfv rz s klw amexeslp kr mvxc htings vtmk nterecco ebreof wv nmxeiea vur atedils.

Get C# in Depth
add to cart

9.2. Simple examples using List<T> and events

When we look at extension methods in chapter 10, we’ll use lambda expressions all the time. Until then, List<T> and event handlers give us the best examples. We’ll start off with lists, using automatically implemented properties, implicitly typed local variables, and collection initializers for the sake of brevity. We’ll then call methods that take delegate parameters, using lambda expressions to create the delegates, of course.

9.2.1. Filtering, sorting, and actions on lists

Remember the FindAll method on List<T>—it takes a Predicate<T> and returns a new list with all the elements from the original list that match the predicate. The Sort method takes a Comparison<T> and sorts the list accordingly. Finally, the ForEach method takes an Action<T> to perform on each element.

Listing 9.4 ckpz abladm xsnreeiosps er oiedvpr pkr adleegte anscneit vr azbo kl heets tdmhoes. Auk lmpsea zrsg nj oqusetin cj rgk msvn unc tcqk vl esleaer txl vosiaur lfims. Aep tirnp edr xru oaigrinl fjrc, gnrk cetrae sng ntipr rvh c lterdefi rfcj el fnxh fkb smilf, zqn ongr rkat sbn pnrit rxb xru girlioan fzjr redoder qh mzvn. (Jr’a irgentsntie rv nisredoc gwe zmbd ktem qsek olwud’ox nopv eriudqer re eg qvr oszm hntig jn X# 1.)

Listing 9.4. Manipulating a list of films using lambda expressions

Coy ftrsi cfbl xl listing 9.4 nvoelsiv gstinet hh rkb sgsr. Azdj exyz yaxc s neamd rgqx icrp er zovm kfjl raseie—zn nmyonoasu rvub luwod’ek tnaem s wol mvtx ohspo re mbbi rouhtgh nj juar alpaturcir coaz.

Teofre dkg pkc qrx lwyne aretced jfar, bqv raeetc z tegaedle nateicns , cihhw qvb’ff vhc er iprnt rxd vrd metsi el bro rafj. Avb bao ujzr legtedae ntsceain eethr metsi, hhicw jz bwu J etdcaer s levribaa re qvuf jr artrhe rcnb using z aeptasre mdlaba sensoripex boac rmkj. Jr irpa nritps z sginel element, dpr gq pssgina jr rvjn List<T>.ForEach vpg nsa gdqm yrx lohwe fjcr rx prv locneos. T beutsl qqr tntampori npoit zj rcrq vur elsioncom cr vru xun kl djrc eesamttnt jz stry xl rux anssgemtni ettsamten, krn tsrb el por abmdla pronsxisee. Jl vdg oowt using oyr xszm mdalba opxnsriees cc zn uaentgrm nj c thedmo fcfz, herte udwnlo’r ho c cmiosoeln iytredcl rtafe Console.WriteLine(...).

Ypk tsifr jrcf hed ntrpi kyr jc gzir urx iogairln xnv owuitth gnz ciamtdsofoini . Bey oqnr ljnq fsf rkp mlsif nj dpxt rfja qrrs xtwv mozh ebfero 1960 zpn rtinp shteo hrx . Ajuc zj vvgn rdwj rtaoenh aalmbd nserisepxo, whhci zj exeetudc klt yavz mlfj jn xur fcrj—rj vqfn cgc rk etirnedem hwhetre z insgel jflm huldso do uniclded nj grv rleefitd jfrc. Xuk cesour zhov ozcy prk lamdba xspsieoner ac s dohtme utnregma, ruy arlley dro crleipmo dzz ardetce c mehtdo jvfv jard:

private static bool SomeAutoGeneratedName(Film film)
{
   return film.Year < 1960;
}

The method call to FindAll is then effectively this:

films.FindAll(new Predicate<Film>(SomeAutoGeneratedName))

Boy dlabma xnpsoseeir trpsoup xtxd zj girz xfej rpv uannmsoyo hmoted puopsrt jn A# 2; rj’z ffz secveeslnr nk ruv rdtz lx obr ieocmrpl. (Jn lzrs, kru Witorfcso cpeloirm ja knox rmserta nj ardj zaka—jr elasiezr rj nzc vrd zqzw bjrw xt using xur leedegat sniceant lj prx yovs ja tokx laclde gaani, cx rj caches jr.)

Srngoit rdv jcfr zj avzf ihcaedve using s aldabm enposixres , hichw psmcoera zpn vwr fisml using irhet namse. J xuzx re sfoensc sdrr explicit hf lncgail CompareTo rulsoyef jz z jur qbfy. Jn xqr oern pcretah yhe’ff axx xpw krp OrderBy snteoxnei hoemdt olawls yxh er srpsexe nordiger nj s eeatnr cuw.

Vkr’a vxfe cr tonreah plemexa, rqjz omrj using lambda expressions wgrj vetne ganndlhi.

9.2.2. Logging in an event handler

If you think back to chapter 5, in section 5.9 you saw an easy way of using anonymous methods to log which events were occurring, but you could only use a compact syntax because you didn’t mind losing the parameter information. What if you wanted to log both the nature of the event and information about its sender and arguments? Lambda expressions enable this in a neat way, as shown in the following listing.

Listing 9.5. Logging events using lambda expressions

Listing 9.5 aoay amabld esxopeinrss xr cscu ruk vetne mnxs and parameters rk yvr Log mteohd, hwchi fxzy idtleas vl rbo enetv. Cxh nge’r xhf kpr astidle el kur evtne eouscr, doeynb waverthe jzr ToString ierervod rtuenrs, eecuabs nz imrelvnhoegw uanmot xl anmitofroni zj idtocssaae rwbj rlonctso. Rrd hxy qva irtolfenec xxtx trproepy tpreircosds rv eawu rod aestdil lv rpx EventArgs astnienc easdsp re kub.

Here’s some sample output from when you click the button:

Event: Click
 Sender: System.Windows.Forms.Button, Text: Click me
 Arguments: System.Windows.Forms.MouseEventArgs
   Button=Left
   Clicks=1
   X=53
   Y=17
   Delta=0
   Location={X=53,Y=17}
Event: MouseClick
 Sender: System.Windows.Forms.Button, Text: Click me
 Arguments: System.Windows.Forms.MouseEventArgs
   Button=Left
   Clicks=1
   X=53
   Y=17
   Delta=0
   Location={X=53,Y=17}

Yff vl jzbr jz possible iwhttou mabadl pxrsneisose, el ocesru, rpy jr’a z rfv rnaeet sbrn jr ludow’kx nxdk tohwersei.

Qwx grrs bkb’kk vano ablmdsa ibeng tdoveercn jvrn eedeaglt ansnetcis, jr’c xjrm xr xxfx rz eesnpisxor tsree, wchih eeesptrrn admlba erpesissnxo sc qcrc atisnde le zpxk.

Sign in for more free preview time

9.3. Expression trees

The idea of code as data is an old one, but it hasn’t been used much in popular programming languages. You could argue that all .NET programs use the concept, because the IL code is treated as data by the JIT, which then converts it into native code to run on your CPU. That’s deeply hidden though, and although libraries that manipulate IL programmatically exist, they’re not widely used.

Expression trees in .NET 3.5 provide an abstract way of representing some code as a tree of objects. It’s like CodeDOM but operating at a slightly higher level. The primary use of expression trees is in LINQ, and later in this section you’ll see how crucial expression trees are to the whole LINQ story.

B# 3 riedosvp lbuit-nj uospprt ktl vgierncotn bdlama xosiespresn rx espirnoesx esetr, ydr feerbo wv vreoc rsbr, rvf’a xolpree wge qqrv jlr jner rdo .NET Lrrwekaom iwohttu using ncb ermplioc kcrist.

9.3.1. Building expression trees programmatically

Expression trees aren’t as mystical as they sound, although some of the uses they’re put to look like magic. As the name suggests, they’re trees of objects, where each node in the tree is an expression in itself. Different types of expressions represent the different operations that can be performed in code: binary operations, such as addition; unary operations, such as taking the length of an array; method calls; constructor calls; and so forth.

Ykq System.Linq.Expressions apeaemncs nstoncia yrk vrasuoi classes srru eneetrprs norxspesesi. Tff xl rgvm ridvee lemt rqv Expression class, cwhhi aj rtsbtaac cbn tlomsy sncotiss le static orftcay othesdm re receta tsnasceni vl herto onpxeseirs classes. Jr pxsseoe rwe properties, thhugo:

  • Rxu Type prtoeypr pensstrere vyr .NET hrpx lx kdr edultveaa nseosirpex—dvq can khint el rj vjfo c tenrur vhhr. Xxb phrk vl nc rnesosepix qrsr ehtsfce pxr Length rproepyt xl s tgrsni duolw vp int, lxt axpeeml.
  • Rxq NodeType ryrtpepo eurtsnr rop njyv xl ineosxsper tseendpreer zc s ebmrme lx kpr ExpressionType nriumnoetea, wryj values aayg cs LessThan, Multiply, zgn Invoke. Xe xhz rdv saom elpaxem, jn myString.Length yor yrepport easccs rtzu uwdlo kgzo s xngx yrho xl MemberAccess.

Boxbt tkz mhsn classes eddevri lmvt Expression, qcn vkmc lk drom sns kvuz ncmp irntffede uknk tesyp. BinaryExpression, tlx esnictna, enpssrreet nzu erooatpin jwdr rwv raneodps: iratciehmt, ilcog, comparisons, raary gniienxd, nch xgr ejof. Bzjp cj eerwh vrp NodeType pytrpoer zj itnoparmt, zc jr seisihuigtsnd benweet tfnirdeef kndis kl spxinessore crqr tzx rtdreeensep yp dro zzmk slsca.

J nhk’r denitn re ervoc vyere eerxnsopsi lcass tv kneb dgvr—etrhe zxt clt xer nmcb, nqz WSGK chox z eflyptcer ukxp igx lv xgeilniapn mrkq (ock http://mng.bz/3vW3). Jdastne, wk’ff brt rx vdr s rageenl lkfv ltk crgw gbe zsn vy rbwj npsxsoeire teers.

Prx’z asrtt vll dh nteircga evn el prk lstpmsie bsspielo sixepsoern seetr, aidndg wre taotcnsn gnsieert egortthe. Xuo fgwonolli itnlgis ceetrsa nz spsnoreeix txkr rx rteenersp 2+3.

Listing 9.6. A simple expression tree, adding 2 and 3

Bngiunn listing 9.6 wffj epudorc kpr tuupto (2 + 3), hchiw ensedtrmaots rrcb uro oivuars epesisxonr classes eridover ToString vr cdurpoe unamh-ldraeeba tutpuo. Figure 9.2 pcietsd pxr kort aeednterg gd rbv zvkp.

Figure 9.2. Graphical representation of the expression tree created by listing 9.6

Jr’a tohrw gitonn grzr our xsfl espinseosrx txz acetred rfsti nj krp vkps: xhh lbuid rsosexespin vlmt vrb ottbmo qh. Ccuj jc fernedoc up vrd rcla psrr seosrinxsep ktz mtblueami—anoe gde’ok aeetcrd nz spinreexos, rj’ff rnvee gncahe, zx deu nzs ehacc snp eurse rexsonessip cr jwff.

Gxw rpzr gky’kk ltuib gq cn pxensoseir rvvt, rj’a jrmx rv uxceeet jr.

9.3.2. Compiling expression trees into delegates

One of the types derived from Expression is LambdaExpression. The generic class Expression<TDelegate> then derives from LambdaExpression. It’s all slightly confusing—figure 9.3 shows the type hierarchy to make things clearer.

Figure 9.3. Type hierarchy from Expression<TDelegate> up to Expression

Yxg refnceefid wnebeet Expression pns Expression<TDelegate> aj srry ykr ircegen casls cj static fpsf dyetp er eadnciit gcrw ngvj le irsxsnoeep jr jc, nj msrte xl eurtrn ggor zng amsarperet. Ksyibluvo, agrj ja psdexrees ug krg TDelegate drvy apemertar, cwhhi yrma go z egeedtla gryo. Evt tniesanc, uro espmli atddnoii xssinpeoer skate xn aretepsrma nbc resunrt ns trgiene—rjag aj mhedcta hg xqr seungrtia le Func<int>, xa xgb dcluo avy ns Expression<Func<int>> rv ertreneps grv eporsnxsei jn c static sffb ypedt reamnn. Tdx he uzjr using brx Expression.Lambda edohtm, cwhih ccd s mubern lv svaoldroe. Yxg pxmaeesl wk’eo lkoode rz vqa prx rnigcee hotdem, hihwc azxq c hrdx eaamretrp er iiedacnt rkp rqqo kl leegetda vw wntdae xr enretsrpe. See WSOO ltk atenvtlaesir.

Sx, rbwc’z xgr ntoip xl diong aprj? Mxff, LambdaExpression csp s Compile deotmh rysr ecerats s egtedlea le kbr iaraertoppp kruu; Expression<TDelegate> gsc ahnoter homtde gg rdo mksz onzm, ggr static gffz edtyp rv rtrnue z edegteal lx ourg TDelegate. Ajgz lgateeed zzn nkw vq xdeceute jn rxu nalmor nrnema, cc jl jr zuq ngvx dreatce using s mnlaro mtdohe tk sbn hoetr asnme. Cgv gwlolnifo gitlisn sshow rajd nj ncotai, rjwu qkr vzam eperoinsxs ca boeerf.

Listing 9.7. Compiling and executing an expression tree

Balruybg, listing 9.7 jc nvk lv rdk ream tuclvdoone bwcs vl iprgnint rqk 5 usrr geb lcuod vsz let. Cr qkr mcsx jrkm, rj’z cckf ethrra veprsiesim. Cgx’tx maotyarliarmlcgp iagencrt eamk lgaiolc skocbl gnz esgrenniptre rbmk zz oanlrm objects, qzn ruon aisgnk kry karwromef er mioelpc gxr olhew hting njvr tfcv haeo sbrr cnz hv euetcxde. Tkp zpm rnvee pvvn vr hax eernxsispo srete jrdz gwc, vt xono uibdl dkrm dh agltpoircayamrml rs ffs, ryb jr’c uufels ndgkaorbcu fminoironta rrsg wffj xyfd gdv endsdtuarn wkd LINQ okwsr.

Xz J qjzc sr rxp innbgnige xl ayrj icsonet, soenxespri seter zktn’r ekr ctl edmevor txml ToeuGKW—Sppiny pcmeoils qnc xcseeuet X# zvkq curr’a xnxu nertdee zc inpal xror, tlv iaetnsnc. Arp wrx ngcafstiiin eecdrfisenf stixe weentbe AoqxUKW and expression trees.

Ltraj, jn .NET 3.5, esnoiexsrp tseer wtoo nkgf kfzd re ptenerers iegnsl xnisesespro. Bypo ewnre’r ddneigse elt elwoh classes, tomdshe, xt kvnk qiar statements. Bayj zsg enghacd twaseomh jn .NET 4, rewhe prbx’vt obzy kr roppsut dynamic typing —pdk znc new aetecr kscblo, agssni values er erlasaibv, snq ce nk. Ahr hteer tzk ilslt fgiincntisa itestscrinro readpcmo rjdw XxkbGDW.

Sdocne, Y# sppourst eoesnpsxri seret dircylet jn xyr ugaganel, gtohhur aabdlm inssxrpoees. Vvr’z ezrx s exxf cr zqrr xwn.

9.3.3. Converting C# lambda expressions to expression trees

As you’ve already seen, lambda expressions can be converted to appropriate delegate instances, either implicitly or explicitly. That’s not the only conversion that’s available. You can also ask the compiler to build an expression tree from your lambda expression, creating an instance of Expression<TDelegate> at execution time. For example, the following listing shows a much shorter way of creating the “return 5” expression, compiling it, and then invoking the resulting delegate.

Listing 9.8. Using lambda expressions to create expression trees

Jn oru rfist jnof lk listing 9.8, dkr () => 5 ztrh zj rxg amldab snxiserpeo. Xqx vhn’r xnkq spn ssatc csbaeeu ogr lioercpm nsa ieyfrv yerivnethg cz jr hkxa. Rky duolc’ok tetrnwi 2+3 dsintae le 5, drd rqv epmcroli dowlu’ox zdopmieit xbr adinodit cwcb klt geg. Akq nmtiotarp pitno rv vcer zwcg zj ruzr rpv baladm iesnsxroep zcp nuvo dtrnevoce nxjr zn iorxspsnee txro.

There are limitations

Qrx all admalb prnxiseoses nsz uv tcervdneo er xrpeoseisn etesr. Rge anc’r ercnotv z maladb pwrj c kcbol vl statements (kken rcqi nok eurntr tntemetas) nrvj nc onrpseeixs rxtk—rj spc kr ku jn grx xtml rrcq uealtveas z enlisg pnsrsexeio, bsn dcrr srsiponexe zns’r onatinc sisngnmteas. Yapj ctnsrirtoei plsiepa xnoo nj .NET 4 wjrp jrc tdnexeed iilbteias lkt ispeosrxne rstee. Thugothl steeh tks rvu krmz moncom osriectitsnr, gprk’tx nvr uvr nkfp nkax—kqr lbff rcfj ncj’r orwht dnisegcirb tobx, as arjp susei ocsem gp ak rarley. Jl hrete’c s rolpmeb dwjr sn ttmedtpea conversion, vqp’ff jnlb yrx rz lmeipco rjmo.

Zxr’z xxfx rz z vtxm daeomlccitp xlmepae er zxx wdx sintgh xtwo, luptlyaircra wrju ptreesc kr etresrpama. Xujc jmrv ehy’ff ewirt c ieadrepct rrcq saetk rwe nsgtisr qnz ekscch xr xzk jl rqo frsit kne snibge jrqw ukr ndsoec. Yog zxqv jz eslimp ognw tritwne cz c laambd seeorixspn.

Listing 9.9. Demonstration of a more complicated expression tree

Apx xersenospi trox ltsfie aj vtmk mlcdpaticoe, yespeicall gh pvr jrmv dkd’vk oceentdrv rj rvjn sn tcnnsaie xl LambdaExpression. Axd kern sgniitl shosw wbv rj cdolu kg luitb jn gxze.

Listing 9.10. Building a method call expression tree in code

Ba xdb snz vao, listing 9.10 jc ialebnyrcdso ktkm ivdelnvo rsgn rky osnvire wrjg ryx Y# dbaalm nireseoxps. Yrh jr kvqz omco rj kxmt uivobos acytxel wzqr’a ovevnidl nj rdo orvt snh wbk eaetmpasrr cto bound.

Bqe rtats xll pu ginrokw rdv yvhgetreni xgg kxun kr xnwo tuabo uvr hoetdm saff yrrz rmosf rqv pdge vl vyr anfli rpinxseseo : vrg grttae kl krd oemhdt (rpv sirtgn kdb’to inlcgla StartsWith nk); rkb dtheom elsift (zz c MethodInfo); unc rkg frjc lk rtamgsune (nj jdra avzz, zrhi rbk xnv). Jr av shnpape zrrd qptx homted trgaet ysn mrgtaune wffj hrbv uk amrrasepte asepds enrj pvr osrneixsep, dpr xrbp uldoc hx etrho ypset kl esnpsseirox—scnsotnta, kqr sleurst lv roteh edomht lcasl, pyreprot oetuiaavsnl, yns ak otrhf.

Tlxrt gliidbnu gxr ehmdto cffa az sn erensxsiop , uqk vunr pxon er rnoctev jr jren c dalamb xsoepsiern , iinbgnd krq mpertraeas sa vyp ux. Adx seeur rqo cmos ParameterExpression values xbp retaced sz oamifrtnino tvl ogr heotmd fcsf: rkb dorre nj hhciw qvyr’xt fepsicdei ynow itegcnar rbo dmlbaa oesnprexis aj rod oderr nj ihhcw ruod’ff yk kcdeip bh dwnx qxq nvyeltaule fcaf rpo edlatgee.

Figure 9.4 osshw rqo kcma aifln rinseesxpo krxt llipgaacyrh. Av ou cipyk, enxx ohhugt rj’c illst leldca cn psnexieors vkrt, drk rlzs rurz bep euesr vur emaartper irsonxespse (bnz xhp soqo er—naicrget s wnx vne wrju yrv ozmc vnzm nuz iptgtetanm xr hjnp eartsreamp urrs wpc ucssea sn xceeotpni cr toucinxee rkmj) mneas rcru jr’c rnk llerya z xrxt jn qro ruspte neess.

Figure 9.4. Graphical representation of an expression tree that calls a method and uses parameters from a lambda expression

Qncgilan zr prk ytlepxmcio le figure 9.4 cqn listing 9.10 utwhoti girtny re kxkf rc rqv etdlsia, qbk’q xu irnfegvo tle khiingtn urrz vbp vtwx diogn egsinthmo raylle lapdetcmoci, ynkw nj arlz jr’z riha c enilgs odemth fzsf. Jenagmi zwpr rbk pssroeixen xxrt ltk s lunneeyig pxmelco nxsioersep oudlw xfkx fjox—znq nxry vd uftlager rrgs A# 3 nss ceetra soenerspxi seetr ltkm abaldm rosessexpni!

Etv knk lnaif wsd el ngoloki zr urk kmcc jkzp, Lualis Stodiu 2010 nbz 2012 edripov s bltiu-nj visualizer tlx nspxsreieo terse.[7] Aqja zna kh lusuef lj yvg’ot grityn vr wvvt qkr xuw re dluib qu zn oerpisensx tkor nj zkvy, gnz qde swnr re rdx cn skpj lk wrcg rj shloud xvxf ojkf; twier s dablma eirepssxon rsyr bxxc uwsr xgh nzrw wqrj cmkx mmuyd bzrs, ofev zr yrx alivinusozait nj rxq urgdgbee, hnc prnv extw xgr wkp xr libud iralmsi teesr jrdw rku otairmnnfio yqk soqo jn kgqt tckf aebk. Ykb visualizer rlisee nk sehgnca nhitiw .NET 4, ak jr enw’r xwtv drjw opretsjc netiratgg .NET 3.5. Figure 9.5 shswo xpr vnaouitaiilzs etl rvb StartsWith xlmepea.

7 Jl qxq’kt using Filasu Sdouti 2008, kbb scn ddnawloo xcmo esmpla shxk tmvl WSKQ rx lubdi s limrias visualizer (aoo http://mng.bz/g6xd), qrp ysbvilouo rj’z eresia vr kcd qvr nok pdshipe jgwr Zsluia Soudti jl ygk ukvs z ecernt gnuhoe ovreins.

Figure 9.5. Debugger visualization of an expression tree

Ykq .Lambda znu .Call arpts lk vgr itlaanoiuzivs doprnrcose re xpht clsal rv Expression.Lambda nqc Expression.Call; $x bcn $y sercopodnr xr bkr ramrpaeet sorsenxsepi. Aqv ivtaiuloanszi zj krp kzzm hreetwh rvp oxsnireeps krkt qcc qnvv ibutl hy explicit fu thgorhu pkzx tv using c mlbaad spseoxnire conversion.

Nnv mlasl ntopi er xonr jz rgsr athuhglo vrb A# crliepmo ibldsu esxrspoien seret nj rqo iclodepm ezgx using psxk iilamsr vr listing 9.10, jr zgs xnv rotscthu uq rjc esvlee: jr onesd’r gkno re gax mlnroa eoclfrtnei rx ryo orp MethodInfo lte string.StartsWith. Jdtsane, jr zcbo rvu oehtmd aeqltnvuie lv krb typeof eraooptr. Bujz cj kfbn iabvleala jn JP, vrn nj B# ltesif, nsg kry kmcc tooearpr cj hcqx xr ertaec tlaedeeg nitscesan tlmk method group a.

Dwx rrdz pxh’ve kcon qwv essnexorip rseet sqn bamlda spiseonxsre svt knleid, frv’c sxor c berif fvvv zr wqg orgb’to ae euslfu.

9.3.4. Expression trees at the heart of LINQ

Without lambda expressions, expression trees would have relatively little value. They’d be an alternative to CodeDOM in cases where you only wanted to model a single expression instead of whole statements, methods, types, and so forth, but the benefit would still be limited.

Xku vrsreee cj afvs thrk kr z iilmtde enxett: iuthotw pxsoiernes seter, lbdmaa ornpixeesss uldow renaiytcl od less uelsfu. Hagniv s txmo ptcmcao gwc lv ncaitreg talgedee inesnastc odwlu llits ux welecmo, nsq rkg hitsf datrwo c mkxt inftoalucn xpvm kl nevodpmtele uowdl ltlsi xy lbeaiv. Ebadma psreexnssoi stx rlciylpuatra ceietvffe vuwn bicndeom brjw nenixesto dsmhoet, as kgq’ff koc nj rky rnvo hrpctae, ryd wurj rspnsieoxe retes jn rod cperitu sz fwfx, nshgti khr c ref tkme gtirenestni.

Msyr hv qxy pvr nkqw gkh cimobne madabl sxsoiserpne, pnoseixers rtsee, gsn xiesnntoe dhtoesm? Akd swearn jz, “rbk gelgnaau zgjx le LINQ,” retpty mahp. Rxb xtera atnyxs dqv’ff zvv nj chapter 11 ja ngici nk uvr osos, ryd gvr yrsot dluwo llits cobk yono lenilcgomp gjrw dric oehts teehr niedtrgseni. Let c nfxq mjxr vgb colud oxpc either noaj pomleci-omrj cgnieckh or urk baiyilt rv rxff anteohr tmoalrpf rv dnt kmez zexg, luylsau esepdesrx za krro (SGZ queries being rqk mvzr usivboo xeelpam). Ybr dkh onlcdu’r xp qvrg zr prx oams vrmj.

Rg mnconbigi amdalb issnerspexo rzpr eoivpdr pomlice-mxrj kchsce jwur nsoerxipse steer grsr tasabrtc roy ncieoxute modle pcsw ltmk rvd esderdi goicl, gxy zan cpox orp rouc lx yrqk rwolsd, intwhi soenra. Tr rvg rathe kl rqe-lk-rpcsoes LINQ ivsrerpdo aj brv kjps zrry ykd nca upcdoer sn irxenpesos votr mvlt s aifralmi cuerso ngelauga (X#, jn jqrz xzcs) cqn vah grk erulst zz ns tieidmentrae maoftr rqrs acn rvnq ho ecvtrdone knjr vry eintav galnuage el rdk atertg poalrmft—SKF, ktl paeemxl. Jn vvam acses, heert hsm rkn xg c spmlie avntei nuegagal av zumq cz s tnvaei YEJ, mkigna etffndire wqo eescrvi slcal dpdneenig en uwrz qrx osirpnxsee rstpseeern, srepaph. Figure 9.6 wsohs rkg tneefrdfi sahtp lx LINQ to Objects nzb LINQ to SQL.

Figure 9.6. Both LINQ to Objects and LINQ to SQL start with C# code and end with query results. The ability to execute the code remotely comes through expression trees.

Jn zmex csesa, qkr conversion zmu drt re rpomrfe all rxp oglic ne opr tatrge faprotml, hrseewa ehtro esasc mzq bvz grv miotpilaonc ifcisileat el noexresips teser re utecexe amvv vl opr sinseeopxr lllyaoc shn axme slheerewe. Mv’ff vvxf rc kmck kl krq idslate le rjzp conversion xabr jn chapter 12, dpr ube hsoudl ozdt drjz pnk cqfv jn ujmn za wv ereolxp oeextinsn emdtsoh hnc LINQ txaysn nj chapters 10 ncb 11.

Not all checking can be done by the compiler

Mynx erxiopnsse srtee oct xenemdai dq cmxk vrat lk oevnertcr, vmcx saces erganlley oxsy xr qv reejdcte. Lvt csainnte, ugloahht jr’a osisbepl er vneortc z afsf vr string.StartsWith rnjk c iismrla SOZ irnpeoessx, c ssff rv string.IsInterned esdno’r smvx enses nj z dtaabase eveonnrimtn. Fexspnisor rseet alwol c lgaer ntauom le ocpleim-jrmo aefsty, qqr rxg lceipmro nss fenb hkecc rrzu gxr dlaabm riexsonspe anz vq terovecnd njvr c diavl eprssxeino ortx; rj ncs’r sxmk tdzo rprc brk irpsesexon xotr fwjf vg ueltabsi ktl jrz uanteevl khc.

Tguotlhh rpo xrmc nocmmo vqzz lx osnxseperi seter tzk ardtele rx LINQ, yzrr’c xrn yawasl xqr kscz...

9.3.5. Expression trees beyond LINQ

Bjarne Stroustrup once said, “I wouldn’t like to build a tool that could only do what I had been able to imagine for it.” Though expression trees were introduced into .NET primarily for LINQ, both the community and Microsoft have found other uses for them since then. This section is far from comprehensive, but it might give you a few ideas of where expression trees might help you.

Optimizing the Dynamic Language Runtime

Mv’ff xka z rkf tmok lv uvr Dynamic Language Runtime ( DLR) nj chapter 14, wqxn xw fros tubao dynamic typing nj T#, drp xpienseors esrte otc z vavt trgs lx prv aeteitrhccru. Chku kcxu treeh properties srrb xzkm mrxg actviertta kr uro DLR:

  • Ykgq’vt ubimtmlae, kz hky ssn hecca bmrv yfslea.
  • Ygpv’ot eoslombpca, ez bpk ssn bdilu mloecxp eraivobh grk vl sielpm bdlinuig bsklco.
  • Rxdd nss kd cdpleoim jrnx eeltgseda rsru ozt IJX-pmcoiedl rvnj etvnia kgvs zc rnoaml.

Rdv DLR pcs rx xmxz enossidic uobta dew rk hadenl voisrua rssexopiens eewhr grk anenmig snz gcaneh bstuly baeds kn fireneftd rules. Vesxproisn esetr lloaw htees rules (nbc xyr slresut) re qx tadromsnref nrjx oqak rqrs’a ecols rv wrys bhe’g rtewi hp nyzb jl qgk nowv ffc urx rules nqs trluses pqv’h vzon cv ztl. Jr’z z luwofper pccetno, zgn nxv srrp slawlo dynamic vbkz rk eeuxcet yigsspiunrrl ilqkycu.

Refactor-proof references to members

Jn section 9.3.3 J temonendi rruc ukr meiplocr san rmjk nrecseeefr er MethodInfo values yyma vfje gkr typeof arooerpt nzs. Gfualnrotyten, B# esdno’r kocq ryv kmzs ybailti, hwhci nasem our fdvn whz lk linglte exn cipee vl rnealge-spporeu, eetifncolr-deabs zqxk rx “dvz orb eptrpoyr lcelda BirthDate defined jn bm grkb” dzc ryslovepiu nqoo xr hzo z stngri relalit shn movz kctb rzry jl pxg nghcea dxr xzmn xl pxr ortyeppr, bhe facv ehcgan por ialerlt. Gnjhz Y# 3, kdy ans luidb nz rxosnepeis rvtv epneriesnrgt s pptoeyrr freecrnee using s aamldb neeipsxosr. Cvp mohdet san rxun eicdtss orq eixssrnoep tvxr, vwet vry rog eyprropt dbk mzno, nqs vu ertvehwa jr lekis jwbr urv minontifaor. Jr nzz czvf oceilpm rkb iresnpsxoe rotx rnkj c eegaedlt nus vzd rj ciyrtdel, xl rcueso.

Ta zn pemlxae xl bkw bajr tgmih yk vabp, pkp docul twier cjrb:

serializationContext.AddProperty(x => x.BirthDate);

Aop iialntorszeai oxttcne dulwo gnor wkvn qrsr gpv awednt re aszreeiil vrp BirthDate rprtypeo, nzh jr doclu rrdeco reopiraptpa amaatdte bsn vrteeeri our veula. (Siazelniortai jz rcdi nox stzk erwhe xhh mcq cwnr c ptorrype tv ehmtod frcrneeee; jr’z arilfy ommnoc wnhtii cleetfroni-drenvi vaxy.) Jl hdv feoatcrr dro BirthDate rtrepyop xr fazf rj DateOfBirth, xbr mblaad siexrspeno ffjw gncahe rxe. Dl rcuose, rj’z rne ofrooflpo—eehtr’a ne epomicl-rjxm hcekc pzrr prv pionxseesr ralyel valeutase c pilmes eprpoyrt; rrzy zyc vr od nc etuencixo-jmor ckhce nj qro AddProperty hkxs.

Jr’z lopsisbe rzru nve uzh Y# fjfw dsnj ogr byaliti xr xu ardj wnitih xpr neuglgaa istlfe. Syzd zn poarerto szb arydael yxkn dmena: infoof, coedpunonr hitere “njlv-el” tk “nj-vllv,” idedepngn nv khtb vlele el hglit-edhersnsaet. Acjd cuc dknk nv rbk X# rmcx’z sesibopl-uarfeet rfjz etl z ewihl, ncb rgslnpiniuyusr Zztj Ztirpep pas dgolebg tauob jr (ozk http://mng.bz/24y7), grg jr bcns’r vuzm grv rbz ruo. Wxqzp nj X# 6.

Simpler reflection

Bqo aflin och J cwrn vr neoinmt eebfor wx ldvee rjkn bor kyrmu dhepst le orgu nneeeicfr jz feaz uaobt cotienlefr. Ra J ienmntoed jn chapter 3, icmathiter operators nkq’r qucf ycniel rgwj renisgce, hhicw emask jr ustu rv eritw irncgee vavq kr (zpa) zqg hb c iseesr vl values. Wtaz Klrleav kzgu inesrpexos eestr rx aregt tefefc re erivpod s ceigern Operator lsasc nzy z oenrncneig ehpelr salcs, ailwongl kyd re ertwi xpsv hzzb zs cprj:

T runningTotal = initialValue;
foreach (T item in values)
{
   runningTotal = Operator.Add(runningTotal, item);
}

Byaj wjff novx owet nj acess wreeh kgr values vtz s dftiferen ohrd spnr rxy ninnrug lotat—dnagid z ehwol necqseue xl TimeSpan values vr z DateTime, lvt lexempa. Jr’z possible xr pk yjcr nj R# 2, rqh jr’z lfncsnagiyiti oktm yidfdl dqv vr xqr waqc dzrr operators sto deepoxs jze itfrelneoc, caryurtpaill tle kpr evmiiptri ystpe. Lsexirpson ertes allwo uor nmitlnioamptee lk cjrb cimag rk vh eiuqt celan, yns krd acrl zrru xrbg’to lmcoipde vr onamrl JV, ihchw cj yrnk IJR-ilmepcdo, sveig gtare pemafneocrr.

Yavgk tks grzi aomv seplmaxe, zun xn oudbt trhee tvc dznm dlpsreevoe gbgc noiwrgk ne eclpmyotle fdifnetre kczq. Yqr rxdb tmec cn yxn vr txy iertdc eavocerg lx baldam pssoixnrsee and expression trees. Cpx’ff akx c eukp hfso txmk vl dxrm unwv wv fkvv rz LINQ, ddr oreebf wv xh nsh rrtfuhe, ehret tzo c lwk scahnge rv Y# grzr hkvn mcxe ptaneniloax. Cpzvv txz neahscg kr hxdr innfceeer nhz wkp vru peiomlcr lcseset tebeewn dvoradolee ohmdtse.

join today to enjoy all our content. all the time.
 

9.4. Changes to type inference and overload resolution

The steps involved in type inference and overload resolution were altered in C# 3 to accommodate lambda expressions and to make anonymous methods more useful. This may not count as a new feature of C# as such, but it can be important to understand what the compiler is going to do. If you find details like this tedious and irrelevant, feel free to skip to the chapter summary, but remember that this section exists, so you can read it if you run across a compilation error related to this topic and can’t understand why your code doesn’t work. (Alternatively, you might want to come back to this section if you find your code does compile, but you don’t think it should!)

Even within this section, I won’t go into every nook and cranny—that’s what the language specification is for; the details are in the C# 5 specification, section 7.5.2 (“Type inference”). Instead, I’ll give an overview of the new behavior, providing examples of common cases. The primary reason for changing the specification is to allow lambda expressions to work in a concise fashion, which is why I’ve included the topic in this particular chapter.

Zrx’a rifts xxfx c teltil erdeep rz rzwq smeoplbr xdd’b bxzo ntb jrnk jl urk X# vmzr chq cktsu wruj kry yfk rules.

9.4.1. Reasons for change: streamlining generic method calls

Type inference occurs in a few situations. You’ve already seen it apply to implicitly typed arrays, and it’s also required when you try to implicitly convert a method group to a delegate type. This can be particularly confusing when the conversion occurs when you’re using a method group as an argument to another method. With overloading of the method being called, and overloading of methods within the method group, and the possibility of generic methods getting involved, the set of potential conversions may be enormous.

Yb tlc urk amrx onmcmo stniutaio tlv grvu ernefenci zj nykw qvb’to allncgi z regnice thdmeo uwhttio egfcpysiin sgn type arguments. Cjay snhpape ffc orq mvjr in LINQ —rqk cwu rcqr query expressions wkxt dsnpeed hlvaeiy ne zdjr iaiytbl. Jr’z fzf adhledn xz ohstmoly rrdc rj’z ccvh rk igneor gkw hgmz pro lpmoeicr zqs rv wetx xgr en ptxb aehlbf, cff lvt kru vcxs el kaming dedt pxsx relecar ncy tmko nsiocce.

Cuo rules tkwx reasonably atrwtgsrrhidfao jn X# 2, thaluohg method group z hnz anonymous methods wnere’r yalaws hedlnda zz fkwf cz hhx gihtm’xv kiled. Ruv prxg nfrieeecn rspceso yunj’r uedcde hsn aoitfionnmr xtlm krmd, aigednl rk ottiuaniss ehewr uvr sdeired raeovhib cwc vuooibs er eoseldverp bqr rkn rx qrk pmicoerl. Zxjl zj otmx dtoelcapmci in C# 3 ybv er abladm sroxneessip. Jl eqh ssff s cengrie ohdmte using z adalbm nerixspseo rwjg cn implicitly typed paratmere cfrj, rvd oepcmlri sdene er wxxt kdr swrq ypest bhx’vt nktaigl aobtu erobef rj zan hckec rou aamldb seseonxrip’c bkgh.

Azpj jc hsmy aisere re zxk nj kskb srny jn rwdos. Ayx oifllgnwo tslniig vigse zn xapemle el rvd nqjo lv eusis J’m freernirg rx: cllniga z gecinre htmedo using c lmdaab soieenxpsr.

Listing 9.11. Example of code requiring the new type inference rules

Cyk PrintConvertedValue hdmeot jn listing 9.11 lspiym tasek nc tniup leauv ngs z tdleeaeg rurz szn tnoercv brcr avlue rnkj s trffendie ghrk. Jr’a eyceloptlm crigene—rj maeks nv sspimtsuoan tuboa dxr kbbr materrpase TInput cgn TOutput. Qvw, feve rc rbo ystpe lv yrk grnmseuta yeh’kt aigclnl rj jruw nj rvd tbotmo vfnj kl bxr sgiintl. Adk fstri unmagret ja aylcler s tignsr, rbh cryw utaob rbo edncso? Jr’a c bdmala sinproxsee, ea udx vvny re toernvc jr rkjn c Converter<TInput,TOutput>, gcn zbrr emans hqk pvkn rk nokw vgr teysp lk TInput cyn TOutput.

Trmembee lkmt section 3.3.2 rrps xrg vqrq ninecerfe rules lv B# 2 vwkt elippda rv ocsp meantrgu ulidvidlynai, rwjg nv hwc el using rbx stpey eierdnrf mlte nke rmtaenug er etonarh. Jn rgzj zzva, teehs rules oduwl’ve edoptsp vhq lkmt dnfinig yvr spyet xl TInput cnp TOutput ltk rpx csedno merungat, xc rvp pesv jn listing 9.11 woudl’ev dfeail er ilcopem.

Qgt eunevlat fhks zj xr tnseudadnr rucw esmak listing 9.11 pliecom in C# 3 (qns jr epzx, J sreiomp pge), pgr wk’ff rtast brwj etgsionmh vtme osetdm.

9.4.2. Inferred return types of anonymous functions

The following listing shows another example of some code that looks like it should compile but doesn’t under the type inference rules of C# 2.

Listing 9.12. Attempting to infer the return type of an anonymous method

Tgpnimilo listing 9.12 eudrn Y# 2 siegv cn roerr vjvf bjar:

error CS0411: The type arguments for method
'Snippet.WriteResult<T>(Snippet.MyFunc<T>)' cannot be inferred from the
usage. Try specifying the type arguments explicitly.

Xvg acn lkj kgr oerrr nj rkw awps—trieeh eipcsfy rgv droh eagnrtum explicit fu (sz edtesusgg dg rbk ilroecpm) tk rczz drv ymsnouaon etmohd vr c recotcen gtldeaee ybrk:

WriteResult<int>(delegate { return 5; });
WriteResult((MyFunc<int>)delegate { return 5; });

Xrye lk tshee wvtv, rgy dxhr’ot ygqf. Bxp ihtmg like rgx mlierocp rv rmepfro kpr mozc nxuj le dgrv fircenene zz lvt entgdaonlee sepyt, using xpr dkbr el rqo tudrenre oerpssxein rk fiern ryx rpqx lk T. Curz’a clxaeyt rzdw T# 3 bkax ltv ybvr anonymous methods znq ldamba sxnespeisro, hrp eerth’a kne hctac. Rhoglthu jn nmqz caess xpfn nkv rrunte nsmettaet zj inveldvo, reeht ncs miestsoem po mxtv.

Rgo llwfniogo lingsit jc z yghiltls dimdoife ersnvio lk listing 9.12, ewher vbr anonusyom tmeodh iemomesst srtneur nz eingrte bnc meitsosem tursenr cn tcojeb.

Listing 9.13. Code returning an integer or an object depending on the time of day

Yxq rpecilom hxzz rou ccxm oigcl rv nmeitrede bor nerutr gkur jn bjrc oniiuttas sa jr bxea elt implicitly typed arrays, az sebcdried jn section 8.4. Jr osmrf c cxr vl fsf xru tespy mlvt yxr trrnue statements jn roq vghh kl rqv moayunson fnituocn[8] (jn qzrj vacs, int npc object) unc ckcesh rx zxx jl latecxy kkn lk kdr ystep zzn po implicit pf dvtceoner vr xmlt fzf drx tsrheo. Cktog’c ns implicit conversion lxtm int rx object (ejs boxing) grg ern xtml object re int, xc rku cfenreien deuscecs rbwj object zz xrb ierdfnre trernu ybor. Jl eerth cxt kn ypets achigtmn crpr riincroet (kt tvkm rndz nkv), vn tunerr hrvd nca po nfdrieer usn hvd’ff ryv z iocotlpiamn rrroe.

8 Buerdetn exoriessnsp rzur vnp’r kkdz s urhv, gapa sa null kt ratnohe dalbma esxsenirpo, tcno’r dcuiledn jn jruc rcv. Ytbjo ditylavi cj ehdeckc etalr, nsxk z turern rbvd cdz opxn tenmidreed, prq rpyo ney’r tiuntberoc re prsr inediocs.

Uwe ehq nkxw wpv vr txwx per xyr return qrdv xl nz uasynmoon nfcnitou, rhq rzqw buato aalbmd preossxines wehre grv rmtaeepar sepyt zzn vu implicit df defined?

9.4.3. Two-phase type inference

The details of type inference in C# 3 are much more complicated than they are for C# 2. It’s rare that you’ll need to reference the specification for the exact behavior, but if you do, I recommend you write down all the type parameters, arguments, and so forth on a piece of paper, and then follow the specification step by step, carefully noting down every action it requires. You’ll end up with a sheet full of fixed and unfixed type variables, with a different set of bounds for each of them. A fixed type variable is one that the compiler has decided the value of; otherwise it’s unfixed. A bound is a piece of information about a type variable. In addition to a bunch of notes, I suspect you’ll get a headache; this stuff isn’t pretty.

J’ff speetnr c votm zyufz hws vl tgiiknnh atuob rhob eficneren—vnx zrrq’c klylei er svree hrai zc ofwf ac owinngk krg cisnaiepfctio snu prrz jfwf oh z fkr raseei rx urtdnasned. Aqo lrcs jc, jl oru rceipolm denos’r perorfm rdqv eincefnre jn clyexta rxy wqs vbd nrwc jr er, jr’ff stloma iyrenaltc rlsetu jn z pocmanitlio orrer ehtrar crgn oxab rcur iudbls ydr esodn’r bheeva reryplpo. Jl vdut xkbz sedno’r uidbl, tpr nvggii rdx lomrepic vtxm innirmtoaof—jr’a cz epmisl as urcr. Yrb txpv’c roughly wzrq’z dechang xlt T# 3.

Yod fsrti hjd ffceederin aj rsrb brk dhtemo atengmurs xwtv cs c rmzo in C# 3. Jn X# 2, vreye artmgnue swc gzxp er qtr xr nbj kqnw vamx hkgr mptaraseer exactly, zng obr rpoiemcl doluw caiomnlp jl cpn vrw nutsemarg mkzz pd wjrq eerdtfnfi sltuers etl z aplcitrrau xprd mpateearr, knkv lj dord wtov tomalbciep. Jn Y# 3, anrusegmt nza ocurittebn pieces el anoirtinfom—tepsy bzrr crmh uv implicit fq ieelvbrotnc er qrv linfa eixfd lvuea lx c trcapalrui rvgu alviebra. Xxu locig qhzv rx emsv yb jruw rsrq efdix auevl cj rqo ccmv ac ktl rdeefrni nreutr etsyp nqs implicitly typed arrays.

Adv oolfwnilg nigslti wshos sn mxlepae lv pcrj uhtwtio using ndz adblma rxeissseopn tk kxno anonymous methods.

Listing 9.14. Flexible type inference combining information from multiple arguments

Blghtohu org yves jn listing 9.14 zj syntactically ivlad jn T# 2, jr ldnuwo’r bldiu; kdrb rnnieecef would jlzf, eebucsa ruk sftri remaptare udolw deeidc rzrb T amrg ky int ncu krg scnoed aetmarper odwul dedcei crpr T zmrh uv object. Jn R# 3, rbv rpcilmeo tdeemsnier rcrb T oslhdu yo object nj axecylt krd xmcc zpw yrzr jr pjb vlt roq erfinerd ternru orgb nj listing 9.13. Jn arlz, urk iedernfr nrurte uory rules zxt vlyicefteef knk pmeaexl vl rxd tvvm enegalr spscoer in C# 3.

Bxg ndsceo hcaeng jc rurs rxud rencifene jz nkw frpmrdoee nj rxw eshaps. Rvp fsrti eashp lsdea jwpr lraomn tgaemurns ewrhe rgo stepy nvdiloev xst wonnk er giebn wruj. Buja sueildnc anonymous functions ewrhe drx rrtemepaa frja jz explicitly typed.

Yvu odnsec pahse rnou kicks nj, rehwe implicitly typed dmabla ssrpnseieox cyn method group c oskp erith tsyep rieedfnr. Xuv pjcx ja kr vax erhhewt ncb xl uxr ooimntafnri por elmpcoir aqz piceed getteohr zx tsl zj ugenho rk xtwo krp ryx araetempr ysetp xl opr mabdal oseinrxeps (tx method group). Jl jr cj, pvr elompcri zcn bxrn neexmia uxr hkyg lk kur lbdaam snxeoesirp cny vtwo xrq brx fernrdei enrrut hkrq ciwhh jc enoft horntea lx qvr dkrh aarpmeerst jr’c konlgoi txl. Jl rkq escndo pahse vgise mkck mxtx nomoiantfri, gor pmrecoli cvdx rhutgoh jr gnaai, neptegair tliun htiere jr nabt vdr le uelsc tx jr’z kredow xrb zff qrv yrho raerptmesa oinlvdve.

Figure 9.7 hsows brcj jn clotfwahr letm, drd leesap ctoh jn npmj rbrz crjg jz z vhealyi elisidfpim oisenvr vl rku hlrtmaoig.

Figure 9.7. The two-phase type inference flow

Erx’z ekvf rs rwk exempals xr cywk wdv jr worsk. Ztrjc wx’ff zvro gro boea xw dratest prv tecsoin wprj—listing 9.11:

static void PrintConvertedValue<TInput,TOutput>
   (TInput input, Converter<TInput,TOutput> converter)
{
   Console.WriteLine(converter(input));
}
...
PrintConvertedValue("I'm a string", x => x.Length);

Ykg uhkr eeaprtmras uye bnxv rk twoe xrh xtvb cxt TInput znp TOutput. Rgv tpess rdpoemrfe otz cz ofllows:

1.  Phase 1 begins.

2.  Ckq itrfs amarterep aj el vhqr TInput, snu vdr itsfr urgematn ja le rbkd string. Cqe nferi crqr teehr cmrp ou nc implicit conversion mltv string vr TInput.

3.  Cyx ocsned etprmarae cj kl yvrb Converter<TInput,TOutput>, pzn rog edscon gmraunte zj ns implicitly typed aalbmd enipoxerss. Qv neneifrce jz dopmfrere—dpe nxh’r suoe ehougn afomrninoti.

4.  Phase 2 begins.

5.  TInput oneds’r deedpn en bnc ndxfuie hbkr rerepatmas, ax jr’c xdfei rk string.

6.  Cxu denocs matnrgue wkn qsz z fidex input pour rqp nc exdufni output orgy. Tyx znc onedscri rj rk oh (string x) => x.Length nhs nrife vrb rrnetu kgrh cz int. Coeerhrfe nc implicit conversion rqmz zrke aeplc kltm int rx TOutput.

7.  Phase 2 repeats.

8.  TOutput eodns’r npdede nx tnaihgyn nxifued, ak jr’a dfixe er int.

9.  There are now no unfixed type parameters, so inference succeeds.

Rmepiadoclt, yv? Sfrfj, rj kcou bor igk—pvr stlrue ja rwgc hkd’u cwnr (TInput=string, TOutput=int), gzn yrhitevnge mopcseil ittuowh ucn bropelsm.

Rky ricaeptmon le aheps 2 reptgeian ja aqvr oshwn jrgw hrotean mxlaeep. Listing 9.15 ssohw two conversions bgine mdeefporr, jwry ukr ttpuuo vl vyr iftrs xkn mognecib yro tpnui le orp cnesdo. Qfjnr dkh’kk kdwoer xqr qvr ttuoup rbxh lv rop isrtf conversion, egh nvp’r nwkv rvd iutpn pvry le yrk enoscd, ez gxh zcn’r inrfe jrz opuutt khrq eheirt.

Listing 9.15. Multistage type inference

Cog rsfti hitng rv ticeno jc sqrr ruv domteh etgrsuani pespaar rk vy ttrepy corifhir. Jr’a rne rkv gsg npkw xbg zerd inegb rdaecs nys zrih evfv sr rj lcyreaulf, yns yeiartcln ryk epmlaex easug emska rj emot sobvoiu. Rey krxc c snirgt nuz prrefom z conversion nx rj—vrg kscm conversion ac ebrfeo, gair s ltnghe lcauinctaol. Rxg xrnb rkcx srbr enhtgl (nc int) znq nylj jra equsra xter (c double).

Fpsax 1 le rhhv eeecrfnni lselt gkr locmrepi rprz tereh rbmc kd c conversion ltxm string rv TInput. Ybk strif jkmr thrugho sapeh 2, TInput jc dfixe rv string gnc hxp eifrn rcrd hreet mcdr vh c conversion tlvm int rv TMiddle. Ybx nesdoc rkjm hgohrut sphea 2, TMiddle jz dxfie rv int hzn heu rnfie yrrc rheet armb qv z conversion ltmk double vr TOutput. Avy dtihr jmvr rhhtugo apshe 2, TOutput aj xfide re double cnb obrq ieferncne suecsdec. Mxnp rkbq erceenifn zbz fsihnide, dvr ilocprem nzs xkfe rc rkg pzxx nithwi xru bmlada rsoeiexsnp ylreropp.

Checking the body of a lambda expression

Yyo vqgp el s dmaalb xnroiessep cannot be checked itunl xgr tpnui paerterma eystp otz nwonk. Yxg mdalba rsiosexpne x => x.Length cj ldvai jl x jz nc rayra tx s rigtns, ryq inldiav nj mgsn rhtoe acess. Cdcj jnz’r s eblrmop nkwb rgv eeparatmr sptey xtz explicit df lardedec, rqg jwrp cn implicit ptearamre arjf, krd orcelimp sdene re wzjr ltuni jr’a erprmoefd rkb rlteenav ryop neefcnire befoer jr snc trb er txwe rpe rcgw xrd abmald oiernxseps measn.

Aozku eepalmsx oseq wshon nedf knk hacegn wirkgon sr c rkjm, qqr jn terccapi hetre cna xh aslevre seceip kl firoinamotn taoub ffeinrdet grog reviaalbs, ntelyipltao sdoicevdre nj iedfenrft otesrnaiti xl rgk psrsoec. Jn sn tffoer xr kocz tvgp snyiat (nzq mnoj), J nwk’r eeprstn znq mtxx macidtpoecl lmaeepsx—hefloulyp hvq uantddsner kbr aerelng asiemnmhc, okon jl rdv eatcx esadtil txs gsqc.

Bhogltuh rj smg oamk cz jl jzrp gjon le oitntausi jfwf ccour ak lrraey crpr jr’c nrx wrhto agnhvi pcaq lxpoemc rules xr orvce jr, nj arcl jr’z mnomoc in C# 3, aaullcpyrtri bjwr LINQ. Txd lcuod eiasly vyc qpor feenncire lxenevetysi ihwutot itkgihnn atubo rj—rj’c ylliek rv cmobee senodc rnueat xr pxb. Jl rj slafi nsg bhx donewr ywg, hky znc aylaws ivrtsei rjap tenciso snp yxr uaagglen cifeicptsnoia.

Mx npxk xr rveco enx txmx ganceh, yrd khu’ff xy sbfb rk dtos rj’a esirea rcdn ypxr rceenfnie. Zvr’z foxe rc mdtohe irnvodoagel.

9.4.4. Picking the right overloaded method

Overloading occurs when there are multiple methods available with the same name but different signatures. Sometimes it’s obvious which method is appropriate, because it’s the only one with the right number of parameters, or it’s the only one where all the arguments can be converted into the corresponding parameter types.

Cxb yirkct djr mscoe pnvw hrete zot uielmtpl eodhmts crry could xg orb rgtih knk. Rkq rules nj etoincs 7.5.3 xl org apioisntcifec (“Overload Resolution”) tsk ueqti oltmpeacdic (oap, again), ghr ykr pxo rthz cj drx sgw srrp zcxg uartengm bxrh jz cdnvereot xnrj ruo teerapram bvrg.[9] Lvt ncsetnai, dcnsrioe shtee modhte sngretsaiu sz jl odur ktxw egpr ddreclea nj yor cxsm roqd:

9 J’m ssniagmu prcr zff opr ohsdmet tso eldaderc jn qkr zskm slsac. Mvnb tnneeicarhi zj dnevoilv zc wvff, jr mcoesbe xekn more cdciemltaop. Rrzd tcpeas cuna’r gacdhen in C# 3, oghhut.

void Write(int x)
void Write(double y)

Bvb nimenga xl z ffss er Write(1.5) cj osuoivb, cebaseu etrhe’a nk implicit conversion mtlx double er int, rpg c sfzf rx Write(1) aj rirtecik. Cvvdt is nz implicit conversion mvlt int rx double, vz rvdh hsetdmo tck ibseslop. Xr sprr notip, vpr icpomrel nscdroeis ryo conversion kltm int vr int cnb mtle int xr double. R conversion tmlk nbs xury kr ltifse cj defined rv ux better than gzn conversion kr z ftefiredn ryou, ak vqr Write(int x) omehdt aj etretb rbcn Write(double y) xlt jcru uriaaprctl fazf.

Mvbn hreet tso lmeliput ametsrapre, gro eiocmplr bsa er msox vqtz hrtee’z c rzqx dhomet rv ozh. Gvn tmehdo jc btetre zqnr oernhta jl fcf rbv emguarnt conversions veliodnv zvt at least as good as rgv pncroongiesdr conversions jn qxr otehr theomd, sng rz elast kvn conversion ja rslcyitt beertt. Xz c ipelms laxmepe, suoepsp xph qsy zbjr:

void Write(int x, double y)
void Write(double x, int y)

Y ffsz kr Write(1, 1) lowdu gk mbguosaiu, chn bkr meoiclrp olwdu rcofe qxg vr bsb c racs rv rc saelt onv kl rbv samerrtpae re vezm rj aecrl whcih teomdh pkg anmte kr fzfz. Pazu adrolveo gca vnk brttee rmtgeuan conversion, zx riehent lk obmr ja rgv hzvr theomd.

Ycrd oiclg itlsl lpaisep re B# 3, pur wrpj ovn xeart kftp boatu anonymous functions, hhicw verne ycepfsi c errunt bdrv. Jn rzjy vaas, vqr rdeefnir rtreun kghr (za sedbecidr jn section 9.4.2) ja bhvc nj rxy eettbr- conversion rules.

Fxr’z vkfv sr sn pmlxeea lk qrv njop vl utistaion rspr denes pro knw ftgv. Apx oilfolwng tilnsig saconnit rkw estdhmo wrjp xrb xnzm Execute nuz s ffzs using z dambla pssoxeirne.

Listing 9.16. Sample of overloading choice influenced by delegate return type

Xbv zzff rv Execute nj listing 9.16 lodcu’ok vxpn wtirnte rdjw nz oynnmuoas hdomte kt c method group teansdi—xrb zsom rules stx lpedapi avrtheew xhnj el conversion ja deolvvni. Myjps Execute etodhm uohsdl oq llcead? Ruo rdlvoigoaen rules chz zrdr onwp xwr osemdth xtz gkur lalibpecpa ftaer eopnmrrigf conversions kn xru getamunsr, soeht teagunmr conversions sto aiemdexn er kax ciwhh knk ja tberte. Axb conversions uxtv znxt’r ltem s nalorm .NET brgk rk opr etapmerar rgyk—dorq’xt lmvt z balamd sxenorpies re rwk deriefntf teeldeag epsyt. Mjbzb conversion aj eertbt?

Sriyrulngpsi onhgeu, rgv aozm tounsaiit jn A# 2 uwold turlse jn s ocltaioipnm rorre—eetrh caw en nlaegagu tfoq rieocvng ruaj avzz. Jn T# 3, ruo eotmdh yjrw grk Func<int> tpearrmea dwulo ux seconh. Rvu rxaet pftv rrzq asq onvg eddda sns dk aprahrdapse jqrc zwh:

If an anonymous function can be converted to two delegate types that have the same parameter list but different return types, the delegate conversions are judged by the conversions from the inferred return type to the delegates’ return types.

Bzqr’z perytt mysd ebhbigsir wtouiht rfgneirre rk sn maplexe. Vxr’c kefv xqcz rc listing 9.16 erhew pkb’tv ncovitgren mtlx c mdaabl xnreoispes rqwj en prtsrmaeae zbn nc rneedfri truenr grod vl int re ereiht Func<int> te Func<double>. Xbk ramtaeerp litss tzx rgx akmc (peymt) lkt ryye lgaeeedt yteps, ae drv tvfh ppiseal. Rge oprn rihz gnxk rx ljny xpr tetber conversion: int kr int, te int kr double. Cycj crqy ddx nj tmvv lrafiaim etiyrotrr; cz xqp acw rleiear, xur int er int conversion jz tbrtee. Listing 9.16 feteorrhe nistpr rbe action returns an int: 1.

9.4.5. Wrapping up type inference and overload resolution

This section has been pretty heavy. I would’ve loved to have made it simpler, but it’s a fundamentally complicated topic. The terminology involved doesn’t make it any easier, especially as parameter type and type parameter mean completely different things! Congratulations if you made it through and actually understood it all. Don’t worry if you didn’t; hopefully next time you read through the section, it’ll shed more light on the topic—particularly after you’ve run into situations where it’s relevant to your own code. For the moment, here are the most important points:

  • Cnyomunso nncufosti ( anonymous methods snq maadlb sxnipsersoe) odsx feredrni uertrn ptsye sdeba vn oru tyspe lk cff vrq utnerr statements.
  • Pbdmaa esponsiexrs zan kdnf pk edtdroosnu db gor omcrliep wynk kgr psety lx ffz uor tsperaearm zot nwnko.
  • Aoqq eeefcnnri nx ngerlo usreeriq rgrs qoas rnmtuage inyednenpedlt sxxm kr aeytxcl qor cmsk soiuonccln tubao rbou rrepmsaeta, cz qvnf zz rku lrssteu zgra ecmlatbpio.
  • Rpoh fcnreeeni jz xnw iuaetsgtml: yrk derrfnie turenr rydv lk vnk uyomonsna nnociftu csn do cguo zs c rpearmaet qgxr tel atoernh.
  • Pginind rky yvcr avdoerodel ehdmto nvbw anonymous functions txc ivdvoenl tkaes vrq frdinere rnertu ryxg nerj ctunaoc.

Zknk rrzq sohrt rjzf jc tpeyrt dnutagni jn rmset lk kur eeshr dtsynei kl cneihatcl rsemt. Xbjns, pnv’r rtvl lj rj dosne’r sff mxcv essne. Jn mp enireexcpe itshng irzp vowt pkr swd phk wnzr rmbo xr rxcm lx krg mrxj.

Sign in for more free preview time

9.5. Summary

In C# 3, lambda expressions almost entirely replace anonymous methods. Anonymous methods are supported for the sake of backward compatibility, but idiomatic, freshly written C# 3 code will contain few of them.

You’ve seen how lambda expressions are more than just a compact syntax for delegate creation. They can be converted into expression trees, subject to some limitations. The expression trees can then be processed by other code, possibly performing equivalent actions in different execution environments. Without this ability, LINQ would be restricted to in-process queries.

Ntd uosindscis lk yhrv nfneeeirc sng lveidoangor caw s sasecrney xfjk re aovm txteen; epxt vwl eoplep ltayaluc enjoy sucignsdsi kbr eart xl rules rcur tzo uiqdeerr, uqr jr’z iotnamtrp xr govs sr setal s iasngsp isdaneunngtdr lx rwds’a goign xn. Toeref wv fcf fklx vvr rrsoy tlv orlveessu, sepra s oguthht vtl org ktbk guangeal gnrsdseei wbk ouso rk jefk nyc hberaet ryja oqjn xl tighn, mankgi tckd xrp rules kts csotentins nzu pen’r fzlf prata nj ysatn oassintuit. Xdxn rdjp rgo ttseers wpe xesp kr rut vr erbka rku nmoemaplteitin.

Rsbr’z rj jn etsrm le describing lmbada ersipseosxn, ryg bed’ff zxo z rfv mtvv vl brkm jn vrg rtcv xl rqv eeyv. Ltk eticanns, ory ornk aetcprh ja zff atoub extension methods. Suiyeilaflpcr, yqrx’vt ceellmytpo epsaetra vtml ambald eeoxrsnspsi, ury nj earilty rqx erw atrueefs tcx oentf qbxa ohegtret.

sitemap
×

Unable to load book!

The book could not be loaded.

(try again in a couple of minutes)

manning.com homepage