Summary

Reflection is a very powerful tool in the .NET library. In this article, I try to take some of the mystery out of its basic concepts.

The source code for the Reflection Sample application is available here.

Introduction

.NET supports a concept called Reflection that is quite popular in the Java world. For Windows programmers who've moved to .NET from languages like VB or C++, this concept of type metadata may be a little foreign, and the uses for reflection may not always be obvious.

Since this is new ground for many developers, let's start out with some definitions of the key terms.

Metadata - In the most generic terms, when you have a thing, then the data that describes that thing is called metadata. In the context of .NET and types, metadata describes the information about the type itself: its constructors, methods, properties, etc.

Reflection - Reflection is the process of walking the metadata about a type and finding out things; for instance, how many constructors a type has, and what the parameters are. It also allows you to dynamically create and invoke types.

System.Type

When the compiler builds your code, it also generates all the metadata for you. The metadata information is placed into your assembly, alongside your compiled code. System.Type is the key to unlocking this metadata.

For example, let's pretend we have this class:

public class MagicConverter
{
  public MagicConverter()
  {
    [...]
  }

  public MagicConverter(double quantumFlux)
  {
    [...]
  }

  public double QuantumFlux
  {
    get
    {
      [...]
    }
    set
    {
      [...]
    }
  }

  protected double SpeedOfLight
  {
    get
    {
      [...]
    }
  }

  public SillyClass PerformConversion(double accelerant, double fudgeFactor)
  {
    [...]
  }
}

Now you're interested in discovering something about this type. In C#, you can use the typeof operator to get the Type class that describes this class:

Type typeInformation = typeof(Transmogrifier);

If you have an object in hand but you don't know what type it is, you can call the GetType() method on it to perform the same operation:

public SomeMethod(object o)
{
  Type typeInfo = o.GetType();
}

The Type class contains information about the type itself, as well as some collections that describe what methods, properties, custom attributes, constructors, etc. that class has. So you could write something like this:

public static void Main()
{
  Type typeInfo = typeof(MagicConverter);

  Console.WriteLine("The type's full name is {0}", typeInfo.FullName);
  Console.WriteLine("It has {0} constructors, {1} methods, and {2} properties",
      typeInfo.GetConstructors().Length,
      typeInfo.GetMethods().Length,
      typeInfo.GetProperties().Length);
}

When you run this program, you would expect to see something like this:

The type's full name is MagicConverter
It has 2 constructors, 1 methods, and 2 properties

(If we'd put the MagicConverter in a name space, the full name would include that name space.) Instead, you see something like this:

The type's full name is MagicConverter
It has 2 constructors, 7 methods, and 1 properties

A whole set of mysteries! First, let's tackle the method information.

System.Reflection.MethodInfo

The MethodInfo class contains all the information about a single method in the class. MethodInfo classes are obtained by calling either GetMethod() or GetMethods() on the Type object. The former will search for a method that matches your search criteria; the latter will return an array of MethodInfo for all the methods in the class.

Our first mystery to solve is why our one-method class claims to have seven methods. So let's extend our Main method by looking at the available MethodInfo:

foreach (MethodInfo mi in typeInfo.GetMethods())
{
  Console.WriteLine("{0}(...)", mi.Name);
}

Which results in:

GetHashCode(...)
Equals(...)
ToString(...)
get_QuantumFlux(...)
set_QuantumFlux(...)
PerformConversion(...)
GetType(...)

We've discovered a few things here:

There's clearly some more interesting information to be had yet about these methods.

We can also find out the parameters of the method using our next class.

System.Reflection.ParameterInfo

This class contains the information about a single parameter in a method. From MethodInfo, you can call GetParameters(), which will return an array of ParameterInfo.

Using what we've learned so far, let's extend our foreach loop like this:

foreach (MethodInfo mi in typeInfo.GetMethods())
{
  Console.Write("{0} {1}(", mi.ReturnType.FullName, mi.Name);
      
  bool first = true;
  foreach (ParameterInfo pi in mi.GetParameters())
  {
    if (!first)
      Console.Write(", ");
          
    Console.Write("{0} {1}", pi.ParameterType.FullName, pi.Name);
    first = false;
  }
      
  Console.WriteLine(")");
}

Which results in:

System.Int32 GetHashCode()
System.Boolean Equals(System.Object obj)
System.String ToString()
System.Double get_QuantumFlux()
System.Void set_QuantumFlux(System.Double value)
SillyClass PerformConversion(System.Double accelerant, System.Double fudgeFactor)
System.Type GetType()

That's looking pretty good. You can see that the metadata even includes the names of the parameters we provided to the compiler.

There's a lot more information hidden in MethodInfo that we could use to end up with a more accurate prototype of the method; the downloadable source code shows a few of these.

Constructors are just like methods; when you call Type.GetConstructors(), it returns an array of ConstructorInfo. The ConstructorInfo and MethodInfo classes share a common base class called MethodBase. You can use MethodBase when you don't care whether what you have is a constructor or a method, or the specific types to get more detailed information. The downloadable source code also provides examples of walking the constructor list of a class.

System.Reflection.PropertyInfo

Our other mystery leads us to the PropertyInfo class. Why did our original count only include one property instead of two, and why didn't we get any methods for the missing property with our call to Type.GetMethods()?

The answer lies in the fact that the SpeedOfLight property is protected. When you call the default versions of GetMethods or GetProperties, all you get back is the public methods and properties. If you want information about the non-public methods and properties, then you need to call the overloads of the methods that take a BindingFlags parameter.

BindingFlags is a flags enumeration. Two of those flags -- the one we're interested in -- are Public and NonPublic. If you read the documentation, you'll see that we also need to specify one or both of Instance and Static in order to get data back. Since we're interested in the instance properties, we'll change our calls to get the counts like this:

Type typeInfo = typeof(MagicConverter);
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

Console.WriteLine("The type's full name is {0}", typeInfo.FullName);
Console.WriteLine("It has {0} constructors, {1} methods, and {2} properties",
    typeInfo.GetConstructors(flags).Length,
    typeInfo.GetMethods(flags).Length,
    typeInfo.GetProperties(flags).Length);

Which results in:

The type's full name is MagicConverter
It has 2 constructors, 10 methods, and 2 properties

Aha! We've struck gold! If we list our methods again, now using the flags, we'll see the 3 extra methods include one for get_SpeedOfLight, and two more protected methods that we've inherited from System.Object (Finalize and MemberwiseClone).

Listing properties is somewhat similar to listing methods. See the sample source code for an example of what kind of information is available about properties.

Reflection in the Dispatch Example

To take a concrete example of using reflection in production code, let's take a look at the Attribute Based URL Dispatch article.

There's really two chunks of interesting reflection going on. First we'll examine a cut-down version of the initialization code:

foreach (Assembly assm in AppDomain.CurrentDomain.GetAssemblies()) {
  foreach (Type type in assm.GetTypes()) {
    // Look for our attribute on the class

    object[] attrs =
        type.GetCustomAttributes(typeof(DispatcherAttribute), false);
    if (attrs == null || attrs.Length <= 0)
      continue;

    // Grab the default constructor

    ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes);
    if (ci == null)
      continue;

    // Add a mapping for all the URLs to the given constructor

    DispatcherAttribute attr = (DispatcherAttribute) attrs[0];

    foreach (string url in attr.Urls)
      mappings.Add(url.ToLower(), ci);
  }
}

The outer foreach statement is asking the AppDomain for all the loaded assemblies. We loop over each assembly and ask it (via the inner foreach statement) to give us all the types for that assembly. So far, so good.

For each type, we look for custom attributes that match our DispatcherAttribute. We're screening out looking for types that are decorated with our custom attribute. If it doesn't have that attribute, we're not interested.

Next, we make sure the class has a constructor that takes no parameters (so we can create it later). If it doesn't match that, then we're not interested.

In the full version of this loop, when we're done, we have a collection of types that have our attribute and a parameterless constructor, indexed by the URL.

The next piece of code is called when trying to find a the correct class:

// Find the mapping for the given URL

ConstructorInfo ci = LookupUrl(relativeUrl);
if (ci == null)
  throw new HttpException(404, "Resource not found");

// Cosntruct the object

object o = ci.Invoke(new object[] {});
if (o == null)
  throw new HttpException(500, "Could not construct handler for this URL");

// If it's a factory, then just delegate

IHttpHandlerFactory factory = o as IHttpHandlerFactory;
if (factory != null)
  return factory.GetHandler(context, requestType, url, pathTranslated);

// If it's a handler, then return it

IHttpHandler handler = o as IHttpHandler;

if (handler != null)
  return handler;

// Not a factory or handler, throw a 500

throw new HttpException(500,
      "URL target does not implement IHttpHandler or IHttpHandlerFactory");

The first thing we do is try to find the class that's interested in our URL, which returns back the ConstructorInfo we stashed away during initialization.

We can use the ConstructorInfo to actually create an instance of the class, by calling Invoke on the ConstructorInfo object. You pass in a set of parameters, which in our case is an empty array, because we're calling the parameterless constructor.

Next, we see if the class implements IHttpHandlerFactory or IHttpHandler, as is required by our dispatcher class. If it does, then we call off to the appropriate methods; if not, we throw back a 500 error indicating why we failed.

Conclusion

I hope this article has whetted your appetite for the basics of reflection. As you can see with our real world example, reflection can solve some relatively difficult problems with just a few lines of code. By discovering metadata about types at runtime instead of compile time, we can de-couple things in a way that tends to be very difficult in a more compile-time oriented language like C++.

As with everything in life, reflection comes with its price. For starters, reflection can be rather expensive, so you should do it sparingly, caching the results wherever possible. Also, the de-coupling that happens when things are done indirectly through reflection is a double edged sword, because it can be difficult to figure out which code depends on which other code; debugging reflection-heavy code can also be quite a burden sometimes.

Reflection in moderation is very powerful. You should ask yourself before attempting to solve any problem with reflection, whether code generation wouldn't be a better choice. Kathleen Dollard's web site and book are great resources for .NET developers interested in code generation. The Code Generation Network site contains a lot of useful, platform-agnostic code generation information as well.

Kudos

You can't do these things alone. Thanks to Bernard Vander Beken for being my technical advisor for this article.

About the Author

Brad Wilson is a .NET technology consultant and a frequent contributor to .NET related mailing lists. When not writing at his web log, he spends much of his time looking for inventive ways to break ASP.NET and bend it to his will. :)

Brad lives in beautiful Parker, Colorado. He is an avid game player, including poker, backgammon, cribbage, and Go.

If you have any questions or concerns about his articles, you may contact Brad here.

Brad Wilson