Вы находитесь на странице: 1из 12

7/1/2015

CLRInsideOut:ReflectionsonReflection

ThisisGoogle'scacheofhttps://msdn.microsoft.com/enus/magazine/cc163408.aspx.Itisasnapshotofthe
pageasitappearedon3Jun201507:19:46GMT.
Thecurrentpagecouldhavechangedinthemeantime.Learnmore
Fullversion

Textonlyversion

Viewsource

Tip:Toquicklyfindyoursearchtermonthispage,pressCtrl+ForF(Mac)andusethefindbar.

CLRInsideOut
ReflectionsonReflection
MikeRepass
Codedownloadavailableat:CLRInsideOut2007_06.exe(163KB)
Contents
Reflectionin.NET
InspectingTypesandMembers
InvokingCodeDynamically
PuttingItTogether
EfficientTypeProcessingforSerialization
FinishingUp

Areyourgoalsofcleancomponentizationfrustratedbytheneedtosharetoomuchtypeinformationacross
libraries?Perhapsyouwanttheefficiencyofstronglytypeddatastorage,butitscostlytoupdateyour
databaseschemawheneveryourobjectmodelevolvesandsoyoudliketoinfertypeschemaatruntime?
Doyouneedtoshipcomponentsthatacceptarbitraryuserobjectsandprocesstheminsometype
intelligentway?Doyouwishcallersofyourlibrarycoulddescribetheirtypestoyouprogrammatically?
Ifyoufindyourselfstrugglingtomaintainstronglytypeddatastructures,yetstillmaximizeruntime
flexibility,thenyoulllikelywanttoconsiderreflectionandhowitcanimproveyoursoftware.Inthis
column,IexploretheSystem.ReflectionnamespaceintheMicrosoft.NETFrameworkandhowitcan
benefityourdevelopmentexperience.Iwalkthroughsomesimpleexamplesandfinallytacklethereal
worldscenarioofserialization,inwhichIshowhowreflectionandCodeDomcanworktogetherfor
efficientprocessingofruntimedata.
BeforedivingintoSystem.Reflection,Idliketostepbackabitanddiscussprogrammaticreflectionin
general.Tostart,reflectioncanbedefinedasanyfunctionalityofferedbyaprogrammingsystemthat
allowstheprogrammertoinspectandmanipulatecodeentitieswithoutknowingtheiridentificationor
formalstructureaheadoftime.Thatsamouthful,soIlldissectitpiecebypiece.
First,whatdoesreflectionoffer?Whatcanyouuseitfor?Iprefertoorganizetypicalreflectioncentric
tasksintotwocategories:inspectionandmanipulation.Inspectionentailsanalyzingobjectsandtypesto
gatherstructuredinformationabouttheirdefinitionandbehavior.Typicallythisisdonewithlittleorno
priorknowledgeaboutthem,apartfromsomebasicprovisions.(Forinstance,inthe.NETFramework,
everythinginheritsfromSystem.Objectandanobjecttypedreferenceisyourtypicalstartingpointfor
reflection.)
Manipulationusestheinformationgainedthroughinspectiontoinvokecodedynamically,createnew
instancesofdiscoveredtypes,orevenrestructuretypesandobjectsonthefly.Itsimportanttonotethat,for
mostsystems,manipulatingtypesandobjectsatruntimewillincuraperformancepenaltywhencompared
totheequivalentoperationsdonestaticallyinthesourcecode.Thisisanecessarytradeoffgiventhe
http://webcache.googleusercontent.com/search?q=cache:XzPDGBdXgaUJ:https://msdn.microsoft.com/enus/magazine/cc163408.aspx+&cd=1&hl=en&ct=cln

1/12

7/1/2015

CLRInsideOut:ReflectionsonReflection

dynamicnatureofreflection,buttherearemanytricksandbestpracticesavailableforoptimizingyour
Reflectionperformance(seemsdn.microsoft.com/msdnmag/issues/05/07/Reflectionformoreindepth
informationonoptimizingusageofreflection).
Now,whatsthetargetofreflection?Whatdoestheprogrammeractuallyinspectormanipulate?Inmy
definitionofreflection,Iusedthenovelterm"codeentities"tohighlightthefactthat,fromaprogrammers
perspective,reflectiontechnologycansometimesblurtheconventionaldistinctionbetweenobjectsand
types.Forinstance,atypicalreflectioncentrictaskmightbe:
1. StartwithahandletoanobjectOandusereflectiontoacquireahandletoitsassociateddefinition,a
typeT.
2. InspecttypeTandacquireahandletoitsmethod,M.
3. InvokemethodMonanotherobject,O(alsooftypeT).
NoteImtraversingfromaninstancetoitsunderlyingtype,fromthattypetoamethod,andthenusinga
handletothatmethodtoinvokeitonadifferentinstanceclearlysomethingthatisnotpossiblewith
conventionalC#programmingtechniquesinsourcecode.Illrevisitthisscenariolaterwithaconcrete
example,onceIveexploredtheSystem.Reflectioninthe.NETFramework.
Someprogramminglanguagesofferreflectionnativelyviatheirsyntaxwhereasotherplatformsand
frameworks(suchasthe.NETFramework)makeitavailableasasystemlibrary.Regardlessofhowits
exposed,thepotentialforusingreflectiontechnologyinagivenscenarioisrathercomplex.Aprogramming
systemsabilitytoofferreflectiondependsonmanyfactors:didtheprogrammerexpresshisconceptswell
withthefeaturesoftheprogramminglanguage?Doesthecompilerembedenoughstructuralinformation
(metadata)intheoutputsoastoallowlaterinterpretation?Istherearuntimesubsystemorhostinterpreter
availabletoconsumethatmetadata?Doesaplatformlibraryexposetheresultsofthisinterpretationina
mannerthatsusefulfortheprogrammer?
Ifyoumaintainacomplex,objectorientedtypesysteminyourhead,butexpressyourcodeinsimpleC
stylefunctionswithnoformaldatastructures,thenitsclearlyimpossibleforyourprogramtodynamically
inferthatthepointerinsomevariablev1pointstoanobjectinstanceofsometypeT.Because,afterall,
typeTisaconceptinyourheadthatisnevercapturedinyourexplicitprogrammingstatements.Butifyou
useamoreflexibleobjectorientedlanguage(suchasC#)toexpresstheabstractstructureofyourprogram
anddirectlyintroducetheconceptoftypeT,thenthecompilerwilltransformyourideasintoaformthat
suitablelogiccouldlaterunderstand,asmightbeprovidedbythecommonlanguageruntime(CLR)ora
dynamiclanguageinterpreter.
Isreflectionexclusivelyadynamic,runtimetechnology?Simplyput,noitisnt.Therearemanytimes
throughoutthedevelopmentandexecutioncyclewhenreflectionmightbeavailableandusefultothe
developer.Someprogramminglanguagesareimplementedbystandalonecompilersthattransformhigh
levelcodedirectlyintomachineconsumableinstructions.Theoutputfileconsistssolelyofthetranslated
inputandtheresnosupportlogicavailableatruntimetotakeanopaqueobjectanddynamicallyanalyzeits
definition.ThisisexactlythescenarioofmanytraditionalCcompilers.Sincethereslittlesupportlogic
availableinthetargetexecutable,youcantdomuchdynamicreflection,butthecompilerwillfrequently
offerstaticreflectionforexample,thecommonlyusedtypeofoperatorallowstheprogrammertocheck
typeidentityatcompiletime.
Attheoppositeendofthespectrum,thereareinterpretedprogramminglanguagesthatarealwaysexecuted
byahostprocess(scriptinglanguagesoftenfallintothiscategory).Sincetheprogramsfulldefinitionis
available(astheinputsourcecode)andjoinedtogetherwiththefulllanguageimplementation(asthe
interpreteritself),allthenecessarytechnologyispresenttosupportselfanalysis.Suchdynamiclanguages
frequentlyofferexhaustivereflectionfunctionality,allowingyouarichsetoftoolsfordynamically
analyzingandmanipulatingyourprogram.
The.NETFrameworkCLRanditshostedlanguageslikeC#fallsomewhereinthemiddle.Acompileris
usedtotransformsourcecodeintoILandmetadatawhich,thoughlowerlevelandperhapsless"logical"
thantheoriginalsource,stillcontainmuchoftheabstractstructuralandtypeinformation.Oncethe
http://webcache.googleusercontent.com/search?q=cache:XzPDGBdXgaUJ:https://msdn.microsoft.com/enus/magazine/cc163408.aspx+&cd=1&hl=en&ct=cln

2/12

7/1/2015

CLRInsideOut:ReflectionsonReflection

programislaunchedandhostedbytheCLR,theSystem.Reflectionlibraryofthebaseclasslibrary(BCL)
canconsumethisinformationandreturninformationaboutanobjectstype,atypesmembers,amembers
signature,andsoon.Furthermore,itcansupportinvocationaswell,includinglateboundinvocation.

Reflectionin.NET
Totakeadvantageofreflectionwhenprogrammingwiththe.NETFramework,youusethe
System.Reflectionnamespace.Thisnamespaceprovidesclasseswhichencapsulatemanyruntimeconcepts
suchasassemblies,modules,types,methods,constructors,fields,andproperties.ThetableinFigure1
showshowclassesinSystem.Reflectioncorrespondtotheirconceptualruntimecounterparts.
Figure1System.ReflectionClasses

LanguageComponent
Corresponding.NETClass
Assembly
System.Reflection.Assembly
Module
System.Reflection.Module
AbstractMember
System.Reflection.MemberInfo(baseclassforeverythingbelow)
Type
System.Type
Property
System.Reflection.PropertyInfo
Field
System.Reflection.FieldInfo
Event
System.Reflection.EventInfo
AbstractMethod
System.Reflection.MethodBase(baseclassforeverythingbelow)
Method
System.Reflection.MethodInfo
Constructor
System.Reflection.ConstructorInfo
Thoughquiteimportant,System.Reflection.AssemblyandSystem.Reflection.Moduleareprimarilyuseful
forlocatingandloadingnewcodeintotheruntime.Inthiscolumn,Ileavethembehindandassumethatall
relevantcodeisalreadyloaded.
Forinspectingandmanipulatingloadedcode,thetypicalpatternfocusesonSystem.Type.Normally,you
startbyobtainingaSystem.Typeinstancefortheruntimetypeofinterest(viaObject.GetType).Thenyou
usethevariousmethodsofSystem.Typetoexplorethetypesdefinitionandacquireinstancesoftheother
classesinsideSystem.Reflection.Forinstance,ifyoureinterestedinaparticularMethod,youllwantto
acquireaninstanceofSystem.Reflection.MethodInfoforthatMethod(possiblyviaType.GetMethod).
Likewise,ifyoureinterestedinaparticularfield,youllwanttoacquireaninstanceof
System.Reflection.FieldInfoforthatField(possiblyviaType.GetField).
Onceallthenecessaryreflectioninstanceobjectsareobtained,proceedalongthepathofinspectionor
manipulation,asappropriate.Forinspection,youusethevariousdescriptivepropertiesonthereflection
classestogaintheinformationyouneed(Isthisagenerictype?Isthisaninstancemethod?).For
manipulation,youcandynamicallyinvoketheexecutionofmethods,createnewobjectsbyinvoking
constructors,andsoon.

InspectingTypesandMembers
Letsjumpintosomecodeandexploretheusageofbasicreflectionforinspection.Illfocusontype
analysis.Startingwithanobject,Illretrieveitstypeandthenexploresomeinterestingmembers(see
Figure2).
http://webcache.googleusercontent.com/search?q=cache:XzPDGBdXgaUJ:https://msdn.microsoft.com/enus/magazine/cc163408.aspx+&cd=1&hl=en&ct=cln

3/12

7/1/2015

CLRInsideOut:ReflectionsonReflection

Figure2RetrievingObjectTypeandMembers
Code

usingSystem;
usingSystem.Reflection;
//useReflectiontoenumeratesomebasicpropertiesofatype...
namespaceExample1
{
classMyClass
{
privateintMyField=0;
publicvoidMyMethod1(){return;}
publicintMyMethod2(inti){returni;}
publicintMyProperty{get{returnMyField;}}
}
classProgram
{
staticvoidMain(string[]args)
{
Console.WriteLine("ReflectionDemoExample1");
MyClassmc=newMyClass();
Typet=mc.GetType();
Console.WriteLine("TypeName:{0}",t.Name);

foreach(MethodInfomint.GetMethods())
Console.WriteLine("MethodName:{0}",m.Name);
foreach(PropertyInfopint.GetProperties())
Console.WriteLine("PropertyName:{0}",p.Name);
}
}
}

Output

ReflectionDemoExample1
TypeName:MyClass
MethodName:MyMethod1
MethodName:MyMethod2
MethodName:get_MyProperty
MethodName:GetType
MethodName:ToString
MethodName:Equals
MethodName:GetHashCode
PropertyName:MyProperty

ThefirstthingtonoticeisthatIgotmanymorelinesdescribingmethodsthanyoumightexpectuponfirst
glanceattheclassdefinition.Wheredidtheseextramethodscomefrom?Anyonewellversedinthe.NET
FrameworkobjecthierarchywillrecognizethemethodsinheritedfromtheuniversalbaseclassofObject
itself.(Infact,IactuallyusedObject.GetTypetoretrievethetypeinthefirstplace.)Additionally,youcan
seethegetterfunctionfortheproperty.Now,whatifyoujustwantedthosefunctionsthatareexplicitly
http://webcache.googleusercontent.com/search?q=cache:XzPDGBdXgaUJ:https://msdn.microsoft.com/enus/magazine/cc163408.aspx+&cd=1&hl=en&ct=cln

4/12

7/1/2015

CLRInsideOut:ReflectionsonReflection

definedonMyClassitselfinotherwords,howdoyouhidetheinheritedfunctions?Orperhapsyoujust
wanttheexplicitlydefinedinstancefunctions?
AquicktriptoMSDNonlinerevealsthatyouwanttousethesecondoverloadofGetMethods,which
acceptsaBindingFlagsparameter.BycombiningvariousvaluesfromtheBindingFlagsenumeration,you
caninstructthefunctiontoonlyreturnthedesiredsubsetofmethods.ReplacethecalltoGetMethodswitha
callto:
GetMethods(BindingFlags.Instance|BindingFlags.DeclaredOnly|
BindingFlags.Public)

Asaresult,yougetthefollowingoutput(noticetheabsenceofthestatichelperfunctionandthefunctions
inheritedfromSystem.Object):
ReflectionDemoExample1
TypeName:MyClass
MethodName:MyMethod1
MethodName:MyMethod2
MethodName:get_MyProperty
PropertyName:MyProperty

Whatifyouknowthenames(fullyqualified)ofatypeandmemberaheadoftime?Howdoyoushiftgears
fromtypeenumerationtotyperetrieval?TheexampleinFigure3showshowtousestringliteralsthat
describetypeinformationtoretrievetheiractualcodecounterpartsviaObject.GetTypeand
Type.GetMethod.Takingthecodeinthefirsttwoexamples,youhavethebasiccomponentsnecessaryto
implementaprimitiveclassbrowser.Youcandiscoveraruntimeentityvianameandthenenumerateits
variousrelevantproperties.
Figure3RetrieveTypeandMethodInfoviaStrings
usingSystem;
usingSystem.Reflection;
//useReflectiontoretrievereferencestoatypeandmethodvianame
namespaceExample2
{
classMyClass
{
publicvoidMyMethod1(){return;}
publicvoidMyMethod2(){return;}
}
classProgram
{
staticvoidMain(string[]args)
{
Console.WriteLine(ReflectionDemoExample2);
//notethatwemustusethefullyqualifiedname...
Typet=Type.GetType(Example2.MyClass);
MethodInfom=t.GetMethod(MyMethod1);
Console.WriteLine(TypeName:{0},t.Name);
Console.WriteLine(MethodName:{0},m.Name);
}
}
}
http://webcache.googleusercontent.com/search?q=cache:XzPDGBdXgaUJ:https://msdn.microsoft.com/enus/magazine/cc163408.aspx+&cd=1&hl=en&ct=cln

5/12

7/1/2015

CLRInsideOut:ReflectionsonReflection

InvokingCodeDynamically
Sofar,Iveacquiredhandlestoruntimeobjects(suchastypesandmethods)purelyfordescriptive
purposes,likeoutputtingtheirnames.Buthowcanyoudomore?Howcanyouactuallyinvokeamethod?
Figure4showshowtoacquireaMethodInfoforamemberofatypeandthenuseMethodInfo.Invoketo
actuallycallthemethoddynamically.
Figure4CallingaMethodDynamically
usingSystem;
usingSystem.Reflection;
//useReflectiontoretrieveaMethodInfoforan
//instancemethodandinvokeituponmanyobjectinstances
namespaceExample3
{
classMyClass
{
privateintid=1;
publicMyClass(intid){this.id=id;}
publicvoidMyMethod2(objectp)
{
Console.WriteLine(
MyMethod2isbeinginvokedonobjectwith+
id{0}withparameter{1}...,
id.ToString(),p.ToString());
}
}
classProgram
{
staticvoidMain(string[]args)
{
Console.WriteLine(ReflectionDemoExample3);
MyClassmc1=newMyClass(1);
MyClassmc2=newMyClass(2);

Typet=mc1.GetType();
MethodInfomethod=t.GetMethod(MyMethod2);
for(inti=1;i<=5;i++)
method.Invoke(mc2,newobject[]{i});
}
}
}

Afewkeypointsareinorderforthisexample.First,youretrieveaSystem.Typeinstancefromaninstance
ofMyClass,mc1.Next,retrieveaMethodInfoinstancefromthatType.Finally,whenyouinvoke
MethodInfo,youbindittoadifferentinstanceofMyClass(mc2)bypassingitinasthefirstparameterof
Invoke.
Asmentionedpreviously,thisexampleblurstheboundariesbetweentypeandobjectusagethatyoumight
expectfromtypicalsourcecode.Logicallyspeaking,youretrieveahandletoamethodandtheninvokethat
methodasifitbelongedtoadifferentobject.Thismightbesecondnaturetotheprogrammerexperienced
withafunctionalprogramminglanguage,buttothecoderonlyfamiliarwithC#,itmightseemunintuitive
http://webcache.googleusercontent.com/search?q=cache:XzPDGBdXgaUJ:https://msdn.microsoft.com/enus/magazine/cc163408.aspx+&cd=1&hl=en&ct=cln

6/12

7/1/2015

CLRInsideOut:ReflectionsonReflection

todecoupleobjectimplementationfromobjectinstantiation.
PuttingItTogether
NowthatIveexploredthebasicsofinspectionandinvocation,Illputthingstogetherwithaconcrete
example.Imaginethatyouwanttoshipalibrarywithastatichelperfunctionthatmustprocessobjects
butatdesigntime,youdontknowanythingaboutthetypesoftheseobjects!Itsuptothecallerofthe
functiontoinstructitonhowtoextractmeaningfulinformationfromtheseobjects.Thefunctionwillaccept
acollectionofobjectsandastringdescriptorofamethod.Itwilltheniteratethroughthecollection,calling
themethodoneachobjectandaggregatingthereturnvalueswithsomefunction(seeFigure5).
Figure5ExtractingInformationfromObjects
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Reflection;
namespaceExample4
{
classProgram
{
staticvoidMain(string[]args)
{
//preparesomeobjectsforourfunctiontoprocess
object[]objs=newobject[]{
newIntReturner(1),newIntReturner(2),
newIntReturner(3),newSonOfIntReturner(4),
newSonOfIntReturner(5),newSonOfIntReturner(6),
};
Console.WriteLine(
Attemptingtocomputeaverage,+
passinginarraywith{0}elements.,objs.Length);
intaverage=ComputeAverage(objs,GetInteger);
Console.WriteLine(Foundanaverageof{0}!,average);
}
publicstaticintComputeAverage(
IEnumerable<object>objs,stringmethodname)
{
intsum=0,count=0;
TypefirstType=null;
MethodInfofirstMethod=null;
foreach(objectoinobjs)
{
if(firstMethod==null)
{
firstType=o.GetType();
firstMethod=firstType.GetMethod(methodname);
}
sum+=(int)firstMethod.Invoke(o,null);
count++;
}
//notethatweuseintegerdivisionhere(notfloatingpoint)
http://webcache.googleusercontent.com/search?q=cache:XzPDGBdXgaUJ:https://msdn.microsoft.com/enus/magazine/cc163408.aspx+&cd=1&hl=en&ct=cln

7/12

7/1/2015

CLRInsideOut:ReflectionsonReflection

if(count==0)return0;
returnsum/count;
}
}
classIntReturner
{
protectedintvalue=1;
publicIntReturner(inti){value=i;}
publicvirtualintGetInteger()
{
Console.WriteLine(
GetIntegercalledoninstanceofIntReturner,
Imreturning{0}!,value);
returnvalue;
}
}
classSonOfIntReturner:IntReturner
{
publicSonOfIntReturner(inti):base(i){}
publicoverrideintGetInteger()
{
Console.WriteLine(
GetIntegercalledoninstanceofSonOfIntReturner,
Imreturning{0}!,this.value);
returnvalue;
}
}
classEnemyOfIntReturner
{
protectedintvalue=1;
publicEnemyOfIntReturner(inti){value=i;}
publicvirtualintGetInteger()
{
Console.WriteLine(
GetIntegercalledoninstanceofEnemyOfIntReturner,
Imreturning{0}!,value);
returnvalue;
}
}
}

Forpurposesofthisexample,Illdocumentsomeconstraints.First,themethoddescribedbythestring
parameter(andnecessarilyimplementedbyeachobjectsunderlyingtype)willacceptnoparametersand
willreturnaninteger.Thecodewilliteratethroughthecollectionofobjects,callingthespecifiedmethod
andgraduallycomputingtheaverageofallthevalues.Andfinally,sincethisisntproductioncode,Iwont
worryaboutvalidatingparametersorintegeroverflowwhenaggregatingthesum.
Asyoulookthroughtheexamplecode,observethattheprotocolbetweentheMainfunctionandthestatic
helperComputeAveragedoesnotdependonanytypeinformationapartfromtheuniversalbaseclassof
Objectitself.Inotherwords,youcouldcompletelyalterthetypeandstructureoftheobjectsyourepassing
around,butsolongasyoucanalwaysuseastringtodescribeamethodthatreturnsaninteger,your
ComputeAveragefunctionwillwork!
TheresacriticalgotchatobeawareofthatisrelatedtoMethodInfo(andreflectioningeneral)hiddenin
thislatestexample.Notethat,insidetheforeachloopofComputeAverage,thecodeonlygrabsa
MethodInfofromthefirstobjectinthecollectionandthenbindsforinvocationforeverysubsequentobject.
Ascoded,thisworksgreatitsasimpleexampleofMethodInfocaching.Buttheresafundamental
http://webcache.googleusercontent.com/search?q=cache:XzPDGBdXgaUJ:https://msdn.microsoft.com/enus/magazine/cc163408.aspx+&cd=1&hl=en&ct=cln

8/12

7/1/2015

CLRInsideOut:ReflectionsonReflection

limitationhere.AMethodInfoinstancecanonlybeinvokedoninstancesoftypesfromthesamehierarchy
astheobjectfromwhichitwasretrieved.Youseethisinactionbecauseyoupassininstancesofboth
IntReturnerandSonOfIntReturner(whichinheritsfromIntReturner).
Inthesamplecode,IveincludedaclasscalledEnemyOfIntReturnerthatimplementsthesamebasic
protocolastheothertwoclasses,butdoesnotshareanycommonsharedtype.Inotherwords,theinterface
islogicallyequivalent,buttheresnooverlapinthetypehierarchy.ToexploretheusageofMethodInfoin
thisscenario,tryaddinganotherobjecttothecollection,aninstanceacquiredwith"new
EnemyOfIntReturner(10)"andthenruntheexampleagain.Youllhitanexceptionexplainingthatthe
MethodInfocannotbeusedtoInvokeonthespecifiedObjectbecauseitscompletelyunrelatedtothe
originaltypefromwhichtheMethodInfowasobtained(eventhoughthemethodnameandbasicprotocolis
equivalent).Tomakeyourcodeproductionquality,youmustbepreparedforthisscenario.
Apossiblesolutionmightincludeanalyzingthetypeofallincomingobjectsonyourownandmaintaining
aninterpretationoftheirsharedtypehierarchy(ifany).Whenthenextobjectisofatypedivergentfrom
anyknowntypehierarchy,youneedtoacquireandstoreanewMethodInfo.Anothersolutionmightbeto
catchtheTargetExceptionandsimplyreacquireaMethodInfoinstance.BothofthesolutionsIve
mentionedherehaveprosandcons.JoelPobarwroteagreatarticleforthismagazineintheJuly2005issue
onMethodInfocachingandReflectionperformancewhichIhighlyrecommend.
Hopefully,thisexamplehasdemonstratedthataddingsomereflectiontoyourapplicationorframeworkcan
addalotofflexibilityforlatercustomizationorextensibility.Admittedly,usingreflectioncanbesomewhat
cumbersomewhencomparedtotheequivalentlogicinyournativeprogramminglanguage.Ifyoufeelthat
addingreflectionbasedlatebindingtoyourcodeistoomuchgriefforyouoryourcustomers(because,
afterall,theymustdescribetheirtypesandcodetoyourframeworksomehow),itmightbepossibleto
strikeabalancewithjusttherightamountofflexibility.
EfficientTypeProcessingforSerialization
Nowthatwevewalkedthroughthebasicsof.NETReflectionwithafewexamples,letstackleareal
worldscenario.IfyoursoftwareinteractswithothersystemsviaWebservicesoranotheroutofprocess
remotingtechnology,thenitslikelyyouveencounteredserialization.Serializationisessentiallythe
translationofaliving,inmemoryobjectintoadataformatsuitablefortransmissionoverthewireor
storageondisk.
TheSystem.Xml.Serializationnamespaceinthe.NETFrameworkprovidesapowerfulserializationengine
withXmlSerializer,whichcanconsumearbitrarymanagedobjectsandconvertthemintoXMLwiththe
optionoftranslatingtheXMLdatabackintotypedobjectinstancesatalaterdate,aprocesscalled
deserialization.TheXmlSerializerclassisapowerful,enterprisereadypieceofsoftwareandshouldbe
yourfirstchoiceifyourefacedwithserializationinyourproject.Butforeducationalpurposes,lets
explorehowserialization(oranother,similarinstanceofruntimetypeprocessing)mightbeaccomplished.
Thescenario:youreshippingaframeworkthatneedstoconsumeobjectinstancesofarbitraryusertypes
andconvertthemintosomeintelligentdataformat.Forinstance,supposeyouhaveaninmemoryobjectof
typeAddresslikethis:
(pseudocode)
classAddress
{
AddressIDid;
StringStreet,City;
StateTypeState;
ZipCodeTypeZipCode;
}

Howdoyougenerateanappropriatedatarepresentationforlaterconsumption?Perhapsjustasimple
textualrenderingwilldo:
Address:123
http://webcache.googleusercontent.com/search?q=cache:XzPDGBdXgaUJ:https://msdn.microsoft.com/enus/magazine/cc163408.aspx+&cd=1&hl=en&ct=cln

9/12

7/1/2015

CLRInsideOut:ReflectionsonReflection

Street:1MicrosoftWay
City:Redmond
State:WA
Zip:98052

Ifyouhavefullknowledgeoftheformaldatatypesyoullneedtotranslateaheadoftime(suchaswhen
yourewritingthecode),thenitbecomesveryeasy:
foreach(AddressainAddressList)
{
Console.WriteLine(Address:{0},a.ID);
Console.WriteLine(\tStreet:{0},a.Street);
...//andsoon
}

However,thingsgetinterestingwhenyouhavenopriorknowledgeofthetypesyoullneedtointeractwith
atruntime.Howdoyouauthorgeneralframeworkcodelikethis?
MyFramework.TranslateObject(objectinput,MyOutputWriteroutput)

First,youllneedtodecidewhichtypemembersareofinterestforserialization.Possibilitiesincludeonly
capturingmembersofcertaintypes,suchasprimitivesystemtypes,orprovidingamechanismbywhichthe
typeauthorcandescribewhichmembersneedtobeserialized,suchasbyusingcustomattributesas
markupontypemembers).Youcouldonlycapturemembersofcertaintypes,suchasprimitivesystem
types,orthetypeauthorcandescribewhichmembersneedtobeserialized(possiblybyusingcustom
attributesasmarkupontypemembers).
Onceyouvedocumentedwhichdatastructurememberswillbetranslated,youneedtocodelogicthatcan
enumerateandretrievethemfromanincomingobject.Reflectiondoestheheavyliftingherebyallowing
youtoquerybothdatastructureanddatavalue.
Forsakeofsimplicity,letsdesignalightweighttranslationenginethattakesanobject,grabsthevaluesof
allitspublicproperties,convertsthemtostringsbydirectlycallingToString,andthenproceedstoserialize
thesevalues.Thealgorithmwilllooksomethinglikethis,foragivenobjectnamedinput:
1. Callinput.GetTypetoretrieveaSystem.Typeinstancedescribingtheinputsunderlyingstructure.
2. UseType.GetPropertieswithanappropriateBindingFlagsparametertoretrievethepublicproperties
asPropertyInfoinstances.
3. UsePropertyInfo.NameandPropertyInfo.GetValuetoretrievethepropertiesaskeyvaluepairs.
4. CallObject.ToStringoneachvaluetoconvertit(primitively)tostringformat.
5. Packagetheobjecttypesnamealongwiththecollectionofpropertynamesandstringvaluesintothe
correctserializationformat.
Thisalgorithmsimplifiesmattersconsiderably,butcapturestheessenceofwhatisnecessarytotakea
runtimedatastructureandconvertitintoselfdescribingdata.However,theresacatch:performance.As
mentionedearlier,reflectioniscostlyfortypeprocessingandvalueretrieval.Forthisexample,Iperform
thefulltypeanalysispereveryinstanceofthetypethatssupplied.
Whatifyoucouldsomehowcaptureorpersistyourunderstandingofthetypesstructure,sothatlateron
youcouldtriviallyretrieveitandefficientlyprocessnewinstancesofthattypeinotherwords,skipahead
toStep#3intheexamplealgorithm?Thegoodnewsisthatthisisentirelypossibleusingfunctionalityfrom
the.NETFramework.Onceyouunderstandthetypesdatastructure,youcanuseCodeDomtogenerate
codeontheflythatisboundtothatdatastructure.Youllgenerateahelperassemblycontainingahelper
classandmethodwhichreferencestheincomingtypeandaccessesitspropertiesdirectly(likeanyother
referenceinmanagedcode)andthustheperformancehitfortypeinspectionispaidjustonce.
NowIllamendthealgorithm.Foranewtype:
http://webcache.googleusercontent.com/search?q=cache:XzPDGBdXgaUJ:https://msdn.microsoft.com/enus/magazine/cc163408.aspx+&cd=1&hl=en&ct=cl

10/12

7/1/2015

CLRInsideOut:ReflectionsonReflection

1. AcquireaSystem.Typeinstancecorrespondingtothattype.
2. UsethevariousaccessorsofSystem.Typetoretrievetheschema(oratleastthesubsetoftheschema
thatisofinterestforserialization)suchaspropertynames,fieldnames,andsoon.
3. Usetheschemainformationtogenerateahelperassembly(viaCodeDom),whichislinkedagainst
thenewtypeandprocessesinstancesefficiently.
4. Usecodeinthehelperassemblytoextracttheinstancesdata.
5. Serializethedataasappropriate.
Forallincominginstancesofagiventype,youcanskipaheadtoStep#4foratremendousperformance
boostwhencomparedtoexplicitlyinspectingforeveryinstance.
IvedevelopedabasicserializationlibrarycalledSimpleSerializationthatimplementsthisalgorithmwith
reflectionandCodeDom(itsavailableaspartofthedownloadforthiscolumn).Themaincomponentisa
classcalledSimpleSerializer,whichtheuserconstructswithaSystem.Typeinstance.Withinthe
constructor,thenewinstanceofSimpleSerializeranalyzestheprovidedTypeandgeneratesatemporary
assemblywithahelperclass.Thishelperclassistightlyboundtotheprovideddatatypeandcanprocess
instancesjustasifyoudwrittenthecodeyourselfwithfullknowledgeofthetypeaheadoftime.
TheSimpleSerializerclasshasthefollowinglayout:
classSimpleSerializer
{
publicclassSimpleSerializer(TypedataType);
publicvoidSerialize(objectinput,SimpleDataWriterwriter);
}

Amazinglysimple!Theconstructordoesmostoftheheavylifting:itusesReflectiontoanalyzethetypes
structureandthengenerateahelperassemblywithCodeDom.TheSimpleDataWriterclassisjustadata
sinkforillustratingthecommonserializationpattern.
ForserializinginstancesofasimpleAddressclass,thefollowingpseudocodegetsthejobdone:
SimpleSerializermySerializer=newSimpleSerializer(typeof(Address));
SimpleDataWriterwriter=newSimpleDataWriter();
mySerializer.Serialize(addressInstance,writer);

FinishingUp
Ihighlyencourageyoutoplayaroundwiththesamplecode,especiallytheSimpleSerializationlibrary.Ive
addedcommentsthroughouttheinterestingpartsofSimpleSerializeranditsmyhopethatitwillprove
informative.Ofcourse,ifyourefacedwiththeneedtodoseriousserializationinproductioncode,then
definitelyrelythetechnologiesprovidedinthe.NETFramework(suchasXmlSerializer).Butifyoufind
yourselfneedingtoconsumearbitrarytypesatruntimeandprocessthemefficiently,Ihopeyoullbeable
toadoptmySimpleSerializationlibraryforyourownscenario.
IdliketothankCLRdevelopersWeitaoSu(Reflection)andPeteSheill(CodeDom)fortheirguidanceand
feedback.
Sendyourquestionsandcommentstoclrinout@microsoft.com.
MikeRepassisaProgramManageronthe.NETFrameworkCLRteam.HeworksonReflection,
CodeDom,andvariouspartsoftheexecutionengine.Youcanreachhimonhisblogat
blogs.msdn.com/mrepass.
http://webcache.googleusercontent.com/search?q=cache:XzPDGBdXgaUJ:https://msdn.microsoft.com/enus/magazine/cc163408.aspx+&cd=1&hl=en&ct=cl

11/12

7/1/2015

CLRInsideOut:ReflectionsonReflection

MSDNMagazine
IssuesandDownloads
2007
June
CLRInsideOut:ReflectionsonReflection

http://webcache.googleusercontent.com/search?q=cache:XzPDGBdXgaUJ:https://msdn.microsoft.com/enus/magazine/cc163408.aspx+&cd=1&hl=en&ct=cl

12/12

Вам также может понравиться