Chapter63:Reflection

No Comments

ReflectionisaC#languagemechanismforaccessingdynamicobjectpropertiesonruntime.Typically,reflectionis used to fetch the information about dynamic object type and object attribute values. In REST application, for example, reflection could be used to iterate through serialized response object.

Remark:AccordingtoMSguidelinesperformancecriticalcodeshouldavoidreflection.See https://msdn.microsoft.com/en-us/library/ff647790.aspx

Section63.1: Get the members of a type

usingSystem;

usingSystem.Reflection;

usingSystem.Linq;

publicclassProgram

{

publicstaticvoidMain()

{

varmembers=typeof(object)

.GetMembers(BindingFlags.Public|

BindingFlags.Static|

BindingFlags.Instance);

foreach(varmemberinmembers)

{

boolinherited=member.DeclaringType.Equals(typeof(object).Name);

Console.WriteLine($”{member.Name}isa{member.MemberType},”+

$”ithas{(inherited?””:”not”)}beeninherited.”);

}

}

}

Output(seenoteaboutoutputorderfurtherdown):

GetType is a Method, it has not been inherited. GetHashCode is a Method, it has not been inherited. ToString is a Method, it has not been inherited. Equals is a Method, it has not been inherited. Equals is a Method, it has not been inherited. ReferenceEquals is a Method, it has not been inherited. .ctor is a Constructor, it has not been inherited.

WecanalsousetheGetMembers()withoutpassinganyBindingFlags.Thiswillreturnallpublicmembersofthat specific type.

OnethingtonotethatGetMembersdoesnotreturnthemembersinanyparticularorder,soneverrelyontheorder that GetMembersreturns you.

ViewDemo

Section63.2:Getamethodandinvokeit

GetInstancemethodandinvokeit

usingSystem;

publicclassProgram

{

publicstaticvoidMain()

{

vartheString=”hello”;

varmethod=theString

.GetType()

.GetMethod(“Substring”,

new[]{typeof(int),typeof(int)});//Thetypesofthemethod

arguments

varresult=method.Invoke(theString,newobject[]{0,4}); Console.WriteLine(result);

}

}

Output:

hell

ViewDemo

GetStaticmethodandinvokeit

On the otherhand, if themethod isstatic, you donot needan instance tocall it.

varmethod=typeof(Math).GetMethod(“Exp”);

varresult=method.Invoke(null,newobject[]{2});//Passnullasthefirstargument(noneedforan instance)

Console.WriteLine(result);//You’llgete^2

Output:

7.38905609893065

ViewDemo

Section63.3:CreatinganinstanceofaType

ThesimplestwayistousetheActivatorclass.

However,eventhoughActivatorperformancehavebeenimprovedsince.NET3.5,using

Activator.CreateInstance()isbadoptionsometimes,dueto(relatively)lowperformance:Test1,Test2,Test3…

Typetype=typeof(BigInteger);

objectresult=Activator.CreateInstance(type);//Requiresparameterlessconstructor.

Console.WriteLine(result);//Output:0

result=Activator.CreateInstance(type,123);//Requiresaconstructorwhichcanreceivean‘int’compatibleargument.

Console.WriteLine(result);//Output:123

WithActivatorclass

YoucanpassanobjectarraytoActivator.CreateInstanceifyouhavemorethanoneparameter.

//WithaconstructorsuchasMyClass(int,int,string)

Activator.CreateInstance(typeof(MyClass),newobject[]{1,2,”HelloWorld”});

Typetype=typeof(someObject);

varinstance=Activator.CreateInstance(type);

Foragenerictype

The MakeGenericTypemethod turns an open generic type (like List<>) into a concrete type (like List<string>) by applyingtypeargumentstoit.

//genericListwithnoparameters

TypeopenType=typeof(List<>);

//TocreateaList<string>

Type[]tArgs={typeof(string)};

Typetarget=openType.MakeGenericType(tArgs);

//Createaninstance-Activator.CreateInstancewillcallthedefaultconstructor.

//ThisisequivalenttocallingnewList<string>().

List<string>result=(List<string>)Activator.CreateInstance(target);

TheList<>syntaxisnotpermittedoutsideofatypeofexpression.

WithoutActivatorclass

Usingnewkeyword(willdoforparameterlessconstructors)

TGetInstance<T>()whereT:new()

{

Tinstance=newT(); returninstance;

}

UsingInvokemethod

//Gettheinstanceofthedesiredconstructor(hereittakesastringasaparameter).

ConstructorInfoc=typeof(T).GetConstructor(new[]{typeof(string)});

//Don’tforgettocheckifsuchconstructorexists

if(c==null)

thrownewInvalidOperationException(string.Format(“Aconstructorfortype'{0}’wasnot found.”,typeof(T)));

Tinstance=(T)c.Invoke(newobject[]{“test”})

UsingExpressiontrees

Expressiontreesrepresentcodeinatree-likedatastructure,whereeachnodeisanexpression.AsMSDNexplains:

Expression is a sequence of one or more operands and zero or more operators that can be evaluated to a single value, object, method, or namespace. Expressions can consist of a literal value, a method invocation, an operator and its operands, or a simple name. Simple names can be the name of a variable, type member, method parameter, namespace or type.

publicclassGenericFactory<TKey,TType>

{

privatereadonlyDictionary<TKey,Func<object[],TType>>_registeredTypes;//dictionary, thatholdsconstructorfunctions.

privateobject_locker=newobject();//objectforlockingdictionary,toguaranteethread

safety

publicGenericFactory()

{

_registeredTypes=newDictionary<TKey,Func<object[],TType>>();

}

///<summary>

///Findandregistersuitableconstructorfortype

///</summary>

///<typeparamname=”TType”></typeparam>

///<paramname=”key”>Keyforthisconstructor</param>

///<paramname=”parameters”>Parameters</param>

publicvoidRegister(TKeykey,paramsType[]parameters)

{

ConstructorInfoci=typeof(TType).GetConstructor(BindingFlags.Public| BindingFlags.Instance,null,CallingConventions.HasThis,parameters,newParameterModifier[]{});

//Gettheinstanceofctor.

if(ci==null)

thrownewInvalidOperationException(string.Format(“Constructorfortype'{0}’was notfound.”,typeof(TType)));

Func<object[],TType>ctor;

lock(_locker)

{

beenregistered

if(!_registeredTypes.TryGetValue(key,outctor))//checkifsuchctoralready

{

varpExp=Expression.Parameter(typeof(object[]),”arguments”);//create

parameterExpression

varctorParams=ci.GetParameters();//getparameterinfofromconstructor

varargExpressions=newExpression[ctorParams.Length];//arraythatwill containsparameterexpessions

for(vari=0;i<parameters.Length;i++)

{

varindexedAcccess=Expression.ArrayIndex(pExp,Expression.Constant(i)); if(!parameters[i].IsClass&&!parameters[i].IsInterface)//check if

parameterisavaluetype

{

varlocalVariable=Expression.Variable(parameters[i], “localVariable”);//ifso-weshouldcreatelocalvariablethatwillstoreparaametervalue

Expression.Constant(null)),

varblock=Expression.Block(new[]{localVariable},

Expression.IfThenElse(Expression.Equal(indexedAcccess,

Expression.Assign(localVariable,

Expression.Default(parameters[i])),

Expression.Assign(localVariable,

Expression.Convert(indexedAcccess,parameters[i]))

),

localVariable

);

argExpressions[i]=block;

}

else

argExpressions[i]=Expression.Convert(indexedAcccess,parameters[i]);

}

var newExpr = Expression.New(ci, argExpressions); // create expression that representscalltospecifiedctorwiththespecifiedarguments.

_registeredTypes.Add(key,Expression.Lambda(newExpr,new[]{pExp}).Compile() as Func<object[], TType>); // compile expression to create delegate, and add function to dictionary

}

}

}

///<summary>

///Returnsinstanceofregisteredtypebykey.

///</summary>

///<typeparamname=”TType”></typeparam>

///<paramname=”key”></param>

///<paramname=”args”></param>

///<returns></returns>

publicTTypeCreate(TKey key,paramsobject[]args)

{

Func<object[],TType>foo;

if(_registeredTypes.TryGetValue(key,outfoo))

{

return(TType)foo(args);

}

thrownewArgumentException(“Notyperegisteredforthiskey.”);

}

}

Couldbeusedlikethis:

publicclassTestClass

{

publicTestClass(stringparameter)

{

Console.Write(parameter);

}

}

publicvoidTestMethod()

{

varfactory=newGenericFactory<string,TestClass>();

factory.Register(“key”,typeof(string));

TestClassnewInstance=factory.Create(“key”,”testParameter”);

}

UsingFormatterServices.GetUninitializedObject

Tinstance=(T)FormatterServices.GetUninitializedObject(typeof(T));

IncaseofusingFormatterServices.GetUninitializedObjectconstructorsandfieldinitializerswillnotbecalled.It ismeanttobeusedinserializersandremotingengines

Section63.4:GetaStrongly-TypedDelegatetoaMethodor Property via Reflection

When performance is a concern, invoking a method via reflection (i.e. via the MethodInfo.Invokemethod) is not ideal. However, it is relatively straightforward to obtain a more performant strongly-typed delegate using the

Delegate.CreateDelegatefunction.Theperformancepenaltyforusingreflectionisincurredonlyduringthe delegate-creation process. Once the delegate is created, there is little-to-no performance penalty for invoking it:

//GetaMethodInfofortheMath.Max(int,int)method…

varmaxMethod=typeof(Math).GetMethod(“Max”,newType[]{typeof(int),typeof(int)});

//Nowgetastrongly-typeddelegateforMath.Max(int,int)…

varstronglyTypedDelegate=(Func<int,int,int>)Delegate.CreateDelegate(typeof(Func<int,int, int>),null,maxMethod);

//InvoketheMath.Max(int,int)methodusingthestrongly-typeddelegate…

Console.WriteLine(“Maxof3and5is:{0}”,stronglyTypedDelegate(3,5));

Thistechniquecanbeextendedtopropertiesaswell.IfwehaveaclassnamedMyClasswithanintproperty namedMyIntProperty,thecodetogetastrongly-typedgetterwouldbe(thefollowingexampleassumes’target’is a valid instance of MyClass):

//GetaMethodInfofortheMyClass.MyIntPropertygetter…

vartheProperty=typeof(MyClass).GetProperty(“MyIntProperty”);

vartheGetter=theProperty.GetGetMethod();

//Nowgetastrongly-typeddelegateforMyIntPropertythatcanbeexecutedagainstanyMyClass instance…

varstronglyTypedGetter=(Func<MyClass,int>)Delegate.CreateDelegate(typeof(Func<MyClass,int>), theGetter);

//InvoketheMyIntPropertygetteragainstMyClassinstance‘target’…

Console.WriteLine(“target.MyIntPropertyis:{0}”,stronglyTypedGetter(target));

…andthesamecanbedoneforthe setter:

//GetaMethodInfofortheMyClass.MyIntPropertysetter…

vartheProperty=typeof(MyClass).GetProperty(“MyIntProperty”);

vartheSetter=theProperty.GetSetMethod();

//Nowgetastrongly-typeddelegateforMyIntPropertythatcanbeexecutedagainstanyMyClass instance…

varstronglyTypedSetter=(Action<MyClass,int>)Delegate.CreateDelegate(typeof(Action<MyClass, int>),theSetter);

//SetMyIntPropertyto5…

stronglyTypedSetter(target,5);

Section63.5:Getagenericmethodandinvokeit

Let’ssayyouhaveclasswithgenericmethods.Andyouneedtocallitsfunctionswithreflection.

publicclassSample

{

publicvoidGenericMethod<T>()

{

//

}

publicstaticvoidStaticMethod<T>()

{

//…

}

}

Let’ssaywewanttocalltheGenericMethodwithtypestring.

Samplesample=newSample();//oryoucangetaninstanceviareflection

MethodInfo method = typeof(Sample).GetMethod(“GenericMethod”); MethodInfogeneric=method.MakeGenericMethod(typeof(string));

generic.Invoke(sample,null);//Sincetherearenoarguments,wearepassingnull

Forthestaticmethodyoudonotneedaninstance.Thereforethefirstargumentwillalsobenull.

MethodInfomethod=typeof(Sample).GetMethod(“StaticMethod”); MethodInfogeneric=method.MakeGenericMethod(typeof(string));

generic.Invoke(null,null);

Section63.6:GetaSystem.Type

Foraninstanceofatype:

vartheString=”hello”;

vartheType=theString.GetType();

From thetype itself:

vartheType=typeof(string);

Section63.7:Gettingandsettingproperties

Basicusage:

PropertyInfoprop=myInstance.GetType().GetProperty(“myProperty”);

//getthevaluemyInstance.myProperty

objectvalue=prop.GetValue(myInstance);

intnewValue=1;

//setthevaluemyInstance.myPropertytonewValue

prop.setValue(myInstance,newValue);

Setting read-only automatically-implemented properties can be done through it’s backing field (in .NET Framework name of backing field is “kBackingField”):

//getbackingfieldinfo

FieldInfofieldInfo=myInstance.GetType()

.GetField(“<myProperty>kBackingField”,BindingFlags.Instance|BindingFlags.NonPublic);

intnewValue=1;

//setthevalueofmyInstance.myPropertybackingfieldtonewValue

fieldInfo.SetValue(myInstance,newValue);

Section63.8:CreateaninstanceofaGenericTypeandinvoke it’s method

varbaseType=typeof(List<>);

vargenericType=baseType.MakeGenericType(typeof(String)); varinstance=Activator.CreateInstance(genericType);

varmethod=genericType.GetMethod(“GetHashCode”);

varresult=method.Invoke(instance,newobject[]{});

Section63.9:CustomAttributes

Findpropertieswithacustomattribute-MyAttribute

varprops=t.GetProperties(BindingFlags.NonPublic|BindingFlags.Public| BindingFlags.Instance).Where(

prop=>Attribute.IsDefined(prop,typeof(MyAttribute)));

Findallcustomattributesonagivenproperty

varattributes=typeof(t).GetProperty(“Name”).GetCustomAttributes(false);

Enumerateallclasseswithcustomattribute-MyAttribute

staticIEnumerable<Type>GetTypesWithAttribute(Assemblyassembly){ foreach(Typetypeinassembly.GetTypes()){

if(type.GetCustomAttributes(typeof(MyAttribute),true).Length>0){

yieldreturntype;

}

}

}

Readvalueofacustomattributeatruntime

publicstaticclassAttributeExtensions

{

///<summary>

///Returnsthevalueofamemberattributeforanymemberinaclass.

///               (amemberisaField,Property,Method,etc…)

///<remarks>

///Ifthereismorethanonememberofthesamenameintheclass,itwillreturnthefirst one(thisappliestooverloadedmethods)

///</remarks>

///<example>

///ReadSystem.ComponentModelDescriptionAttributefrommethod’MyMethodName’inclass ‘MyClass’:

///               varAttribute=typeof(MyClass).GetAttribute(“MyMethodName”,(DescriptionAttributed)

=>d.Description);

///</example>

///<paramname=”type”>Theclassthatcontainsthememberasatype</param>

///<paramname=”MemberName”>Nameofthememberintheclass</param>

///<paramname=”valueSelector”>Attributetypeandpropertytoget (willreturnfirst instanceiftherearemultipleattributesofthesametype)</param>

///<paramname=”inherit”>truetosearchthismember’sinheritancechaintofindthe attributes;otherwise,false.Thisparameterisignoredforpropertiesandevents</param>

///</summary>

publicstaticTValueGetAttribute<TAttribute,TValue>(thisTypetype,stringMemberName, Func<TAttribute,TValue>valueSelector,boolinherit=false)whereTAttribute:Attribute

{

varatt= type.GetMember(MemberName).FirstOrDefault().GetCustomAttributes(typeof(TAttribute), inherit).FirstOrDefault()asTAttribute;

if(att!=null)

{

returnvalueSelector(att);

}

returndefault(TValue);

}

}

Usage

//ReadSystem.ComponentModelDescriptionAttributefrommethod’MyMethodName’inclass’MyClass’varAttribute=typeof(MyClass).GetAttribute(“MyMethodName”,(DescriptionAttributed)=>d.Description);

Section 63.10: Instantiating classes that implement aninterface(e.g.pluginactivation)

Ifyouwantyourapplicationtosupportaplug-insystem,forexampletoloadplug-insfromassemblieslocatedin

pluginsfolder:

interfaceIPlugin

{

stringPluginDescription{get;}

voidDoWork();

}

Thisclasswouldbelocatedinaseparatedll

classHelloPlugin:IPlugin

{

publicstringPluginDescription=>”ApluginthatsaysHello”;publicvoidDo

Work()

{

Console.WriteLine(“Hello”);

}

}

Your application’s plugin loader would find the dll files, get all types in those assemblies that implement IPlugin, and create instances of those.

publicIEnumerable<IPlugin>InstantiatePlugins(stringdirectory)

{

varpluginAssemblyNames=Directory.GetFiles(directory,”*.addin.dll”).Select(name=>new FileInfo(name).FullName).ToArray();

//loadtheassembliesintothecurrentAppDomain,sowecaninstantiatethetypeslater

foreach(varfileNameinpluginAssemblyNames) AppDomain.CurrentDomain.Load(File.ReadAllBytes(fileName));

varassemblies=pluginAssemblyNames.Select(System.Reflection.Assembly.LoadFile); vartypesInAssembly=assemblies.SelectMany(asm=>asm.GetTypes());

varpluginTypes=typesInAssembly.Where(type=>typeof(IPlugin).IsAssignableFrom(type)); returnpluginTypes.Select(Activator.CreateInstance).Cast<IPlugin>();

}

Section63.11:GetaTypebynamewithnamespace

Todothisyouneedareferencetotheassemblywhichcontainsthetype.Ifyouhaveanothertypeavailablewhich you know is in the same assembly as the one you want you can do this:

typeof(KnownType).Assembly.GetType(typeName);

wheretypeNameisthenameofthetypeyouarelookingfor(includingthenamespace),andKnownTypeisthe type you know is in the same assembly.

Lessefficientbutmoregeneralisasfollows:

Typet=null;

foreach(AssemblyassinAppDomain.CurrentDomain.GetAssemblies())

{

if(ass.FullName.StartsWith(“System.”))

continue;

t=ass.GetType(typeName);

if(t!=null)

break;

}

NoticethechecktoexcludescanningSystemnamespaceassembliestospeedupthesearch.Ifyourtypemay actually be a CLR type, you will have to delete these two lines.

Ifyouhappen tohavethe fullyassembly-qualifiedtype nameincludingthe assemblyyoucan simplygetit with

Type.GetType(fullyQualifiedName);

Section 63.12: Determining generic arguments of instances of generic types

If you have an instance of a generic type but for some reason don’t know the specific type, you might want to determine the generic arguments that were used to create this instance.

Let’ssaysomeonecreatedaninstanceofList<T>likethatandpassesittoamethod:

varmyList=newList<int>(); ShowGenericArguments(myList);

whereShowGenericArgumentshasthissignature:

publicvoidShowGenericArguments(objecto)

soatcompiletimeyoudon’thaveanyideawhatgenericargumentshavebeenusedtocreateo.Reflectionprovides alotofmethodstoinspectgenerictypes.Atfirst,wecandetermineifthetypeofoisagenerictypeatall:

publicvoidShowGenericArguments(objecto)

{

if(o==null)return;

Typet=o.GetType();

if(!t.IsGenericType)return;

Type.IsGenericTypereturnstrueifthetypeisagenerictypeandfalseifnot.

But this is not all we want to know. List<>itself is a generic type, too. But we only want to examine instances of specific constructedgenerictypes.AconstructedgenerictypeisforexampleaList<int>thathasaspecifictype argument for all its generic parameters.

The Typeclass provides two more properties,IsConstructedGenericTypeand IsGenericTypeDefinition, to distinguishtheseconstructedgenerictypesfromgenerictypedefinitions:

typeof(List<>).IsGenericType//true

typeof(List<>).IsGenericTypeDefinition//true

typeof(List<>).IsConstructedGenericType//false

typeof(List<int>).IsGenericType//true

typeof(List<int>).IsGenericTypeDefinition // false

typeof(List<int>).IsConstructedGenericType//true

Toenumeratethegenericargumentsofaninstance,wecanusetheGetGenericArguments()methodthatreturns an Typearray containing the generic type arguments:

publicvoidShowGenericArguments(objecto)

{

if(o==null)return; Type

t=o.GetType();

if(!t.IsConstructedGenericType)return;

foreach(TypegenericTypeArgumentint.GetGenericArguments()) Console.WriteLine(genericTypeArgument.Name);

}

Sothecallfromabove(ShowGenericArguments(myList))resultsinthisoutput:

Int32

Section63.13:Loopingthroughallthepropertiesofaclass

Typetype=obj.GetType();

//Torestrictreturnproperties.Ifallpropertiesarerequireddon’tprovideflag.

BindingFlagsflags=BindingFlags.Public|BindingFlags.Instance; PropertyInfo[]properties=type.GetProperties(flags);

foreach(PropertyInfopropertyinproperties)

{

Console.WriteLine(“Name:”+property.Name+”,Value:”+property.GetValue(obj,null));

}

About us and this blog

We are a digital marketing company with a focus on helping our customers achieve great results across several key areas.

Request a free quote

We offer professional SEO services that help websites increase their organic search score drastically in order to compete for the highest rankings even when it comes to highly competitive keywords.

Subscribe to our newsletter!

More from our blog

See all posts
No Comments

Recent Posts