Click here to Skip to main content
Click here to Skip to main content

Strong: Reflection without magic strings

, 11 Aug 2008 CPOL
Rate this:
Please Sign up or sign in to vote.
A class that safely retrieves MemberInfos using the C# 3.0 compiler.

Introduction

This is a short article that solves a particular problem. If you're not worried of having magic strings in your code, then there is nothing going on here...

Many methods in the .NET Framework use strings to identify code tokens. Reflection is the obvious area, but there are others like ObjectDataSource. The problem with these strings is that they are opaque to the compiler and IDE. This effectively means that you cannot refactor your code, use simple obfuscation, or even do a "Find All References", and rely on the results. I think this is a major problem, so I found a different way, which I present here.

Background

This solution relies on a new feature in the C# 3.0 compiler. When a lambda expression is assigned to a variable, field, or parameter whose type is System.Linq.Expressions.Expression<TDelegate>, the compiler emits instructions to build an expression tree instead of compiling the lambda to IL. An expression tree is a data representation of a code statement, much like Reflection provides data representations of types. As you can guess from the namespace, this was introduced to support LINQ, but it also enables this solution.

Lambdas are real tokens that the compiler can check, and expression trees are real data structures that can be examined at runtime. So, if you write a lambda that specifies a particular member (field, property, or method), you can then examine the expression tree in the code to find the member.

So, in C# 3.0, the compiler goes just far enough to enable this solution. It parses the source file, but instead of going all the way to IL, it produces output in a form that is easily accessible at runtime.

Here are some links:

Manuel Abadia has written a very good expression tree graphical debugger visualizer [^].

There is also a basic text visualizer in the VS2008 C# samples (MSDN [^]).

The Strong class

The Strong class is quite small, so I have included it here in full. LambdaExpression is the base class of Expression<TDelegate>. The interesting methods are the last four.

public delegate void Action<A, B, C, D, E>
( A a, B b, C c, D d, E e );

public delegate void Action<A, B, C, D, E, F>
( A a, B b, C c, D d, E e, F f );

public delegate void Action<A, B, C, D, E, F, G>
( A a, B b, C c, D d, E e, F f, G g );

public delegate void Action<A, B, C, D, E, F, G, H>
( A a, B b, C c, D d, E e, F f, G g, H h );

public delegate void Action<A, B, C, D, E, F, G, H, I>
( A a, B b, C c, D d, E e, F f, G g, H h, I i );

public static class Strong
{
  public static class Static
  {
    public static FieldInfo Field<T>
    ( Expression<Func<T>> m )
    { return GetFieldInfo( m ); }
    
    public static PropertyInfo Property<T>
    ( Expression<Func<T>> m )
    { return GetPropertyInfo( m ); }
    
    public static MethodInfo Method
    ( Expression<Action> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1>
    ( Expression<Action<T1>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2>
    ( Expression<Action<T1, T2>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3>
    ( Expression<Action<T1, T2, T3>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4>
    ( Expression<Action<T1, T2, T3, T4>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4, T5>
    ( Expression<Action<T1, T2, T3, T4, T5>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4, T5, T6>
    ( Expression<Action<T1, T2, T3, T4, T5, T6>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4, T5, T6, T7>
    ( Expression<Action<T1, T2, T3, T4, T5, T6, T7>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4, T5, T6, T7, T8>
    ( Expression<Action<T1, T2, T3, T4, T5, T6, T7, T8>> m )
    { return GetMethodInfo( m ); }
  }
  
  public static class Instance<TClass>
  {
    public static FieldInfo Field<T>
    ( Expression<Func<TClass, T>> m )
    { return GetFieldInfo( m ); }
    
    public static PropertyInfo Property<T>
    ( Expression<Func<TClass, T>> m )
    { return GetPropertyInfo( m ); }
    
    public static MethodInfo Method
    ( Expression<Action<TClass>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1>
    ( Expression<Action<TClass, T1>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2>
    ( Expression<Action<TClass, T1, T2>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3>
    ( Expression<Action<TClass, T1, T2, T3>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4>
    ( Expression<Action<TClass, T1, T2, T3, T4>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4, T5>
    ( Expression<Action<TClass, T1, T2, T3, T4, T5>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4, T5, T6>
    ( Expression<Action<TClass, T1, T2, T3, T4, T5, T6>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4, T5, T6, T7>
    ( Expression<Action<TClass, T1, T2, T3, T4, T5, T6, T7>> m )
    { return GetMethodInfo( m ); }
    
    public static MethodInfo Method<T1, T2, T3, T4, T5, T6, T7, T8>
    ( Expression<Action<TClass, T1, T2, T3, T4, T5, T6, T7, T8>> m )
    { return GetMethodInfo( m ); }
  }
  
  static FieldInfo GetFieldInfo( LambdaExpression lambda )
  { return ( FieldInfo ) GetMemberInfo( lambda ); }
  
  static PropertyInfo GetPropertyInfo( LambdaExpression lambda )
  { return ( PropertyInfo ) GetMemberInfo( lambda ); }
  
  static MemberInfo GetMemberInfo( LambdaExpression lambda )
  { return ( ( MemberExpression ) lambda.Body ).Member; }
  
  static MethodInfo GetMethodInfo( LambdaExpression lambda )
  { return ( ( MethodCallExpression ) lambda.Body ).Method; }
}

Using the code

The static Strong class has no public methods. Instead, it contains two nested static classes, Static and Instance, that allow you to specify the type of member you want. The Instance class takes a generic type parameter which should be the containing class.

Both classes provide public methods called Field, Property, and Method which are overloaded. These return FieldInfo, PropertyInfo, and MethodInfo respectively. These classes all derive from MemberInfo which has a Name property, amongst others.

The easiest way is to show some examples. They all operate on this class:

class Class
{
  public static int StaticField = 42;
  public static int StaticProperty { get; set; }
  public static void StaticAction() { }
  public static void StaticAction( int i ) { }
  public static int StaticFunc() { return 42; }
  public static int StaticFunc( int i ) { return i; }
  
  public int Field = 42;
  public int Property { get; set; }
  public void Action() { }
  public void Action( int i ) { }
  public int Func() { return 42; }
  public int Func( int i ) { return i; }
}

Finally, here are the examples:

Strong.Static.Field( () => Class.StaticField );
Strong.Static.Property( () => Class.StaticProperty );
Strong.Static.Method( () => Class.StaticAction() );
Strong.Static.Method<int>( i => Class.StaticAction( i ) );
Strong.Static.Method( () => Class.StaticFunc() );
Strong.Static.Method<int>( i => Class.StaticFunc( i ) );

Strong.Instance<Class>.Field( o => o.Field );
Strong.Instance<Class>.Property( o => o.Property );
Strong.Instance<Class>.Method( o => o.Action() );
Strong.Instance<Class>.Method<int>( ( o, i ) => o.Action( i ) );
Strong.Instance<Class>.Method( o => o.Func() );
Strong.Instance<Class>.Method<int>( ( o, i ) => o.Func( i ) );

Conclusion

This is the neatest solution to the magic string problem that I could think of. However, it relies on the C# 3.0 compiler, which might be a problem for some people. You could do something similar with delegates in C# 2.0, but you would have to extract the inner references from the compiled IL code, which is a bit more of a hack. The new expression trees in C# 3.0 make this solution a lot easier.

Thanks for reading this article; I hope you liked it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Nicholas Butler

United Kingdom United Kingdom

I built my first computer, a Sinclair ZX80, on my 11th birthday in 1980.
In 1992, I completed my Computer Science degree and built my first PC.
I discovered C# and .NET 1.0 Beta 1 in late 2000 and loved them immediately.
I have been writing concurrent software professionally, using multi-processor machines, since 1995.
 
In real life, I have spent 3 years travelling abroad,
I have held a UK Private Pilots Licence for 20 years,
and I am a PADI Divemaster.
 
I now live near idyllic Bournemouth in England.
 
If you would like help with multithreading, please contact me via my website:
 
 
I can work 'virtually' anywhere!

Comments and Discussions

 
GeneralYou made the same error I made... PinmemberPaulo Morgado20-Aug-08 6:38 
GeneralRe: You made the same error I made... PinmemberNick Butler20-Aug-08 7:03 
GeneralRe: You made the same error I made... PinmemberPaulo Morgado20-Aug-08 14:13 
GeneralRe: You made the same error I made... PinmemberNick Butler21-Aug-08 0:50 
Interesting, thanks.
 
I did consider Func overloads, but as you can see I decided against them. Not difficult to add if you want them though.
 
----------------------------------
Be excellent to each other Smile | :)

GeneralNeed a better example PinmemberMR_SAM_PIPER18-Aug-08 17:40 
GeneralRe: Need a better example PinmemberNick Butler19-Aug-08 0:58 
GeneralVery similar to what I did with my Dynamic library. PinmemberMarc Brooks18-Aug-08 17:17 
GeneralRe: Very similar to what I did with my Dynamic library. PinmemberNick Butler19-Aug-08 1:06 
Generalmight be a trend of doing reflection PinmemberRednaxelaFX11-Aug-08 6:10 
GeneralRe: might be a trend of doing reflection PinmemberNick Butler11-Aug-08 7:13 
GeneralRe: might be a trend of doing reflection PinmemberRoger Alsing13-Aug-08 4:23 
AnswerRe: might be a trend of doing reflection PinmemberNick Butler13-Aug-08 7:00 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141223.1 | Last Updated 11 Aug 2008
Article Copyright 2008 by Nicholas Butler
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid