§ January 6, 2007

.NET Reflection

What is reflection? Its the image we seen in a mirror. In .NET however its a system that lets you do at runtime what you would normally do in a text editor or visual studio.

Like most object oriented programming languages, .net has the concept of a "type." a Type defines what an object is. Whether your using a primitive such as an integer, or a complex data type, each thing in .net has a type that corresponds to what it is.

public class Foo {}
This class is of type Foo. Now this class is basically pointless... Why? Well because it has no members. Actually it does have members because everything inherits from System.Object, and System.Object has 12 member methods some have overloads, some are static, some are private some are protected others are public. For the sake of simplicity, we wont discuss System.Object, but will stick only to Foo.

public class Foo {
    private int a;
}
Typically we'd never have a class that looked like this. After all, the complier would never let us access Foo::a because it is a private member. What does this have to do with reflection you may ask yourself? The rules of the compiler do not apply to reflection.

To understand reflection, you need to understand what a class is, how encapsulation works (what access modifiers are), what a function is and what variables are. We could go a little further, but thats a good starting point.

Lets take our previous class declaration and spice it up a bit.

public class Foo {
    private int a;

    public int A { 
        get { return a; }
        set { a = value; }
    }

    public bool Bar(string input) {
        return input == "Baz";
    }

    protected void Bing() {
        Console.WriteLine("I'm not very creative");
    }    

    public Foo() { }
}
I've given Foo a makeover. It now has a private variable, a public property, a public and a protected method and a constructor. I've given Foo a few "members."

On the surface (which I wont scratch much) reflection allows you to access, modify or invoke members of an object.

In our editor we can do this very easily:

Foo f = new Foo();
bool b = f.Bar("Baz");
So lets look at what we're doing here.

Where does Foo come from?

At the top (or bottom, depending on your endianness) of the Reflection world is the Assembly (a dll or exe). .NET assemblies are files that (can) contain namespaces and classes. Each assembly contains a manifest (aka a list) of other assemblies that it references. It also contains info about what classes it contains.

When a .NET application starts up, the framework spiders out and loads each assembly in each referenced assembly's manifest until everything referenced is loaded into the current AppDomain. If Foo happens to be in one of these dll's then everything is fine, but what if it isn't? Lets start there. The AppDomain object contains a list of all loaded "Assemblies" (if you cared to look at whats loaded).

Assembly[] asms = AppDomain.CurrentDomain.GetAssemblies();
if an assembly is not loaded, we can load it ourselves (unlike other technologies, once an assembly is loaded into the app domain, it cannot be unloaded until the app domain is unloaded).

Assembly fooAsm = Assembly.LoadFile(@"C:\My Path\foo.dll");
The Assembly object represents the actual dll or exe file and all that is contained within it. From it, we can get a list of types that the assembly contains:

Type[] types = fooAsm.GetTypes();
or request a particular type:

Type fooType = fooAsm.GetType("Foo");
if fooAsm did not contain a class named "Foo" then GetType would return null.

So, now we've loaded the dll / assembly that contained our class, and we've gotten a Type object that contains information about the class Foo. From fooType, we can get a list of Foo's members:

MemberInfo[] memberInfo = fooType.GetMembers();
MemberInfo is the base class for other member specific things like fields, methods, properties and constructors. MemberInfo has a property named "Name" which will match the name you give the member at the source code level (variable a, property A, method Bar, and constructor Foo). The System.Type object can also be used to get specific types of members like fields, methods, constructors and properties.

MethodInfo[] methods = fooType.GetMethods();
PropertyInfo[] properties = fooType.GetProperties();
FieldInfo[] fields = fooType.GetFields();
ConstructorInfo[] constructors = fooType.GetConstructors();
These objects contains more member specific info, such as method parameters, and return type, whether the property has a get and set or just a getter. Each of these Get...() methods also has an overload that takes a BindingFlags parameter. BindingFlags are things that must be considered when looking for a member. if we use the GetMember(field,property,method or constructor) that takes a string, it will look for a member who's name matches that. Reflection uses more than just the name to find the member that you're requesting, the binding flags give additional info as to the members access modifier (public / non-public), whether it's an instance member or a static member, and so on.

FieldInfo aField = fooType.GetField("a", BindingFlags.NonPublic | BindingFlags.Instance);
In the above code, I get a FieldInfo object which represents the private int variable "a" and I can do the same with the method Bar, the parameterless constructor and the property.

Great right? Who the heck cares? What does this have to do with the price of tea? Lets see...

When we simply wrote the code:

Foo f = new Foo();
the compiler had to locate the assembly that contains foo, load the foo type and create an instance. so lets do the same, but using reflection.

Assembly fooAsm = Assembly.LoadFile(@"C:\My Path\foo.dll");
Type fooType = fooAsm.GetType("Foo");
object foo = Activator.CreateInstance(fooType);
Keep in mind that when we load the assembly at runtime, we cannot simply do:

Foo f = (Foo)Activator.CreateInstance(fooType);
unless we add a reference to foo.dll inside visual studio or in our command line compile statement. If we did that, then we don't need to use reflection. We simply need an instance of foo. At the time we code it, we don't care what its type actually is. We know we have an instance of it, and can invoke its methods, access its properties and set its members through reflection (using the MemberInfo, MethodInfo, PropertyInfo, FieldInfo's that we get from its Type object). With reflection, the boundaries of encapsulation no longer apply (but should still be respected). We can access internal objects, we can access and manipulate private class members, or invoke private methods. Back to the matter of creating the instance of foo... One thing to keep in mind about using Activator.CreateInstance is that it actually uses the objects constructor to create the instance that gets returned, so if you CreateInstance as I have above, make sure you have a parameterless constructor in your class. Otherwise, you'll also have to pass in the constructor args as the second set of parameters after the System.Type in CreateInstance.

now lets imagine something crazy that we want to do:

Foo foo = new Foo();
foo.a = 32;
we could never write code that would do this unless it was inside a member (method or property) of Foo (a is a private member and therefore isnt accessible externally to Foo).

Assembly fooAsm = Assembly.LoadFile(@"C:\My Path\foo.dll");
Type fooType = fooAsm.GetType("Foo");
object foo = Activator.CreateInstance(fooType);
FieldInfo fooFieldA = fooType.GetField("a", BindingFlags.NonPublic | BindingFlags.Instance);
fooFieldA.SetValue(foo, 32);
int aVal = (int)fooFieldA.GetValue(foo);
so in the above code, we loaded the foo.dll, got the type that we were looking for (Foo), created an instance of it (using Activator.CreateInstance), got the field that we wanted to set, and used the FieldInfo object to set that field in our instance. then for good measures, we got the value that we just set using that field info object.

Each of the derived objects of MemberInfo (MethodInfo, FieldInfo, PropertyInfo, ConstructorInfo, EventInfo) all use the same concepts. First, you obtain an instance of the object you wish to reflect upon, then you obtain the XXXXInfo object that represents the member you want to manipulate, then you use that object to get, set, invoke or handle whatever your trying to do. That necessitates having a valid instance (which you have) and thats pretty much the basics of using reflection.

This has been an end to end application of the use of reflection. It is not meant as a style guide in how you use reflection. You may very well know everything about the type you're reflecting upon. In that instance, you may just want to reflectively invoke a method, or set a property. You may actually want to modify the look and feel or a Windows control which provides no public means of achieving what you wish to achieve.

Here's a little parting sample using the reflection to access an modify various parts of Foo.
Assembly fooAsm = Assembly.LoadFile(@"C:\My Path\foo.dll");
Type fooType = fooAsm.GetType("Foo");
object foo = Activator.CreateInstance(fooType);
MethodInfo barMethod = fooType.GetMethod("Bar", BindingFlags.Public | BindingFlags.Instance);
// invoke ( instance, method parameters );
bool isBar = barMethod.Invoke(foo, new object[] { "Baz" });

MethodInfo bingMethod = fooType.GetMethod("Bing", BindingFlags.NonPublic | BindingFlags.Instance);
bingMethod.Invoke(foo, null);

PropertyInfo propertyA = fooType.GetProperty("A", BindingFlags.Public | BindingFlags.Instance);
propertyA.SetValue(foo, 33);
int aVal = propertyA.GetValue(foo, null); // second parameter is for if the property was in indexer

All of which equates to the following lines of code:
Foo foo = new Foo();
bool isBar = foo.Bar("Baz");
foo.Bing();
foo.A = 33;
int aVal = foo.A;


If you have any questions, comments, or felt that this article was missing any crucial information, please register (its free) in the FORUMS and post your thoughts there.
Posted 19 years, 3 months ago on January 6, 2007

 Comments can be posted in the forums.

© 2003 - 2024 NullFX
Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License