Click here to Skip to main content
14,833,054 members
Articles / Programming Languages / C# 4.0
Article
Posted 29 Jun 2012

Tagged as

Stats

58.9K views
2K downloads
78 bookmarked

ReflectionHelper

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

Share

About the Author

Paulo Zemek
Software Developer (Senior) Niantic
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
User 1106097926-Oct-14 3:05
MemberUser 1106097926-Oct-14 3:05 
GeneralRe: My vote of 5 Pin
Paulo Zemek26-Oct-14 8:52
mvaPaulo Zemek26-Oct-14 8:52 
QuestionGreat use of Expressions Pin
Sander Rossel10-Jul-14 22:04
professionalSander Rossel10-Jul-14 22:04 
AnswerRe: Great use of Expressions Pin
Paulo Zemek11-Jul-14 2:09
mvaPaulo Zemek11-Jul-14 2:09 
BugCode doesn't compile Pin
imgen11-Jul-12 22:23
Memberimgen11-Jul-12 22:23 
GeneralRe: Code doesn't compile Pin
Paulo Zemek12-Jul-12 3:03
mvaPaulo Zemek12-Jul-12 3:03 
QuestionOne issue Pin
Sacha Barber11-Jul-12 2:25
mvaSacha Barber11-Jul-12 2:25 
AnswerRe: One issue Pin
Paulo Zemek11-Jul-12 2:35
mvaPaulo Zemek11-Jul-12 2:35 
QuestionBrilliant technique ! Pin
BillWoodruff10-Jul-12 15:10
mveBillWoodruff10-Jul-12 15:10 
AnswerRe: Brilliant technique ! Pin
Paulo Zemek10-Jul-12 16:17
mvaPaulo Zemek10-Jul-12 16:17 
QuestionThis technique is something I quite like Pin
Sacha Barber9-Jul-12 0:53
mvaSacha Barber9-Jul-12 0:53 
I am into what you are doing here, useful stuff, I have infact written about something like this in the past : http://sachabarbs.wordpress.com/2011/08/06/generic-method-calls-using-expression-trees-method-caching/

You will have to forgive the bad formatting, I change blog engines and it broke my CSS, and I could not be bothered to go back and fix it

Still very useful : 5
Sacha Barber
  • Microsoft Visual C# MVP 2008-2012
  • Codeproject MVP 2008-2012
Open Source Projects
Cinch SL/WPF MVVM

Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

AnswerRe: This technique is something I quite like Pin
Paulo Zemek9-Jul-12 3:11
mvaPaulo Zemek9-Jul-12 3:11 
GeneralRe: This technique is something I quite like Pin
Sacha Barber9-Jul-12 3:25
mvaSacha Barber9-Jul-12 3:25 
GeneralRe: This technique is something I quite like Pin
Paulo Zemek9-Jul-12 3:27
mvaPaulo Zemek9-Jul-12 3:27 
GeneralRe: This technique is something I quite like Pin
Sacha Barber9-Jul-12 4:02
mvaSacha Barber9-Jul-12 4:02 
GeneralRe: This technique is something I quite like Pin
Richard Deeming16-Jul-12 9:05
mveRichard Deeming16-Jul-12 9:05 
GeneralRe: This technique is something I quite like Pin
Paulo Zemek16-Jul-12 9:10
mvaPaulo Zemek16-Jul-12 9:10 
AnswerRe: This technique is something I quite like Pin
Paulo Zemek6-Aug-12 12:09
mvaPaulo Zemek6-Aug-12 12:09 
GeneralMy vote of 5 Pin
Nicolas Dorier8-Jul-12 12:23
professionalNicolas Dorier8-Jul-12 12:23 
GeneralRe: My vote of 5 Pin
Paulo Zemek8-Jul-12 13:51
mvaPaulo Zemek8-Jul-12 13:51 
GeneralMy vote of 5 Pin
Isabel Furini30-Jun-12 10:12
MemberIsabel Furini30-Jun-12 10:12 
GeneralRe: My vote of 5 Pin
Paulo Zemek30-Jun-12 10:12
mvaPaulo Zemek30-Jun-12 10:12 
GeneralMy vote of 5 Pin
Frank Lindecke29-Jun-12 13:35
MemberFrank 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
MemberJerome Vibert29-Jun-12 10:37 

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.