Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / C# 4.0

ReflectionHelper

Rate me:
Please Sign up or sign in to vote.
4.93/5 (31 votes)
6 Aug 2012CPOL4 min read 71.9K   2.2K   79   30
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:

C#
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:

C#
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:

C#
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:

C#
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:

C#
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:

C#
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Microsoft
United States United States
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.

At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.

Want more info or simply want to contact me?
Take a look at: http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com

Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).

Comments and Discussions

 
GeneralMy vote of 5 Pin
Frank Lindecke29-Jun-12 13:35
Frank Lindecke29-Jun-12 13:35 
GeneralRe: My vote of 5 Pin
Paulo Zemek29-Jun-12 16:06
mvaPaulo Zemek29-Jun-12 16:06 
GeneralMy vote of 5 Pin
Jerome Vibert29-Jun-12 10:37
Jerome Vibert29-Jun-12 10:37 
GeneralRe: My vote of 5 Pin
Paulo Zemek29-Jun-12 13:20
mvaPaulo Zemek29-Jun-12 13:20 
GeneralMy vote of 5 Pin
fredatcodeproject29-Jun-12 6:39
professionalfredatcodeproject29-Jun-12 6:39 
GeneralRe: My vote of 5 Pin
Paulo Zemek29-Jun-12 6:47
mvaPaulo Zemek29-Jun-12 6:47 
SuggestionEdge case to be aware of in regards to properties Pin
kornman0029-Jun-12 5:47
kornman0029-Jun-12 5:47 
GeneralRe: Edge case to be aware of in regards to properties Pin
Paulo Zemek29-Jun-12 5:49
mvaPaulo Zemek29-Jun-12 5:49 
Thanks for pointing that out.
I personally never tried to use that for arrays... but I'll try to correct it so it works even with array length.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.