Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

ReflectionHelper

0.00/5 (No votes)
6 Aug 2012 2  
This class makes getting MemberInfos easy, without the use of magic strings (so it is refactoring friendly) and also allows you to create delegates to do fast accesses to those items, much faster than the normal Invoke, GetValue or SetValue methods.

Introduction

I love reflection. It is useful for many things. In general, it helps me create self-discovering code, be it to do serialization, to fill types with the data returned from the database or even implement an interface at run-time with the right pattern. 

The biggest problem is speed. Reflection is slow. But it should not be. In fact, I don't know why it is so slow, so I decided to do something to make it faster.

How to make the use of reflection faster? 

When we use reflection to get or set a property value, we suffer primary from the untyped problem. That is, the data is get or set as object, so a cast and boxing is usually required. This surely slow down things. But do a simple test, create a method that access a field (of type int, for example) and make it return such value as object. It will be much faster than calling field.GetValue.

Ok... it is not a fair comparison. A field invoke is a kind of virtual call. So, create an interface with a GetValue method, that will receive an "instance" as object, will cast it to the right type, make the field get and return it as int. It will still be much faster than reflection invoke.

But between an interface and a delegate, I consider delegates more cleaner. In fact, I think they are still a little faster. So, considering we can compile Expressions to generate a delegate, I decided to create Expressions to access fields, properties and even methods and do all the necessary casts.

Surely we will lose time compiling the delegate. But if you compile the delegate once, and then call the delegate thousands of times, it is surely worth doing it.  

Using the code  

Ok... if you saw the description of this tip you saw that I will present two things:

  1. How to make get fieldinfos, propertyinfos and so on without using strings and 
  2. How to generate the delegates to do fast accesses. 

Both of those are done by the use of the ReflectionHelper static class.

For example, to get a fieldinfo for a static field, you use this:

ReflectionHelper.GetField(() => Type.EmptyTypes) 

In this case, we will be getting the fieldinfo of the EmptyTypes field. It is not hard to imagine that you have a GetMethod there. So, you can use it like this:

ReflectionHelper.GetMethod(() => Console.WriteLine("")); 

And you will get the method info for that particular overload of the WriteLine method. But, hey, you will probably want to get instance fields, properties or methods, right?

Then you will use the generic version of the ReflectionHelper class. The generic parameter is the type of the instance. So, to get the propertyinfo of the Length property, found in the string class, we do:

ReflectionHelper<string>.GetProperty((str) => str.Length)  

And all of those methods in fact only cast the result of the GetMember method, that looks like this:

public static MemberInfo GetMember<T>(Expression<Func<ForType, T>> expression)
{
  if (expression == null)
    throw new ArgumentNullException("expression");
        
  var body = expression.Body;
      
  switch(body.NodeType)
  {
    case ExpressionType.MemberAccess:
      MemberExpression memberExpression = (MemberExpression)body;
      return memberExpression.Member;
        
    case ExpressionType.Call:
      MethodCallExpression callExpression = (MethodCallExpression)body;
      return callExpression.Method;
        
    case ExpressionType.New:
      NewExpression newExpression = (NewExpression)body;
      return newExpression.Constructor;
  }
      
  throw new ArgumentException("expression.Body must be a member or call expression.", "expression");
 
} 

I think it is not hard to understand how it works.

Surely, I will prefer to have a fieldinfoof keyword than to build an expression to do the work. But, as we don't have such option, I think it is valid.

 

Then, for the interesting part. How do we create fast "invokes" for the memberinfos we get?

We simple create the right expression and then compile it.

For example, the code to create a field get delegate looks like this:

      private static Delegate _GetFieldGetterDelegate(Type instanceType, Type resultType, FieldInfo field)
      {
        var funcType = typeof(Func<,>).MakeGenericType(instanceType, resultType);
        
        var parameter = Expression.Parameter(instanceType, "instance");
        Expression resultExpression;
 
        if (field.IsStatic)
          resultExpression = Expression.MakeMemberAccess(null, field);
        else
        {
          Expression readParameter = parameter;
      
          if (field.DeclaringType != instanceType)
            readParameter = Expression.Convert(parameter, field.DeclaringType);
      
          resultExpression = Expression.MakeMemberAccess(readParameter, field);
        }
 
        if (field.FieldType != resultType)
          resultExpression = Expression.Convert(resultExpression, resultType);
        
        var lambda = Expression.Lambda(funcType, resultExpression, parameter);
 
        var result = lambda.Compile();
        return result;
      }  

In fact, the code could be simpler if I only generated a Func<object, object>, but the code is prepared to do the casts for you (or avoid the casts) depending on the generic arguments you want. It can generate a Func<object, int>, so the cast will be done to the entering parameter, but if the property is already int, no cast will be done on the result.

And to use the code, you will call methods like this:

ReflectionHelper.GetFieldGetterDelegate<MyType, int>(fieldInfo); 

In this case, the returned delegate is the Func<MyType, int>, so the input should be already cast as MyType and the result will be an int. There are many methods like this, so you can get a property getter (the most common one in my cases) or a constructor delegate. 

The full code? Well... it is part of my personal library and originally I only posted that. Now I am only updating the article to put a single unit version that you can add to any project. 

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here