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

Strong: Reflection without magic strings

Rate me:
Please Sign up or sign in to vote.
4.86/5 (17 votes)
11 Aug 2008CPOL3 min read 49.1K   47   12
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.

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

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

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


Written By
United Kingdom United Kingdom
I discovered C# and .NET 1.0 Beta 1 in late 2000 and loved them immediately.
I have been writing software professionally in C# ever since

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.

I can work 'virtually' anywhere!

Comments and Discussions

 
GeneralYou made the same error I made... Pin
Paulo Morgado20-Aug-08 5:38
professionalPaulo Morgado20-Aug-08 5:38 
GeneralRe: You made the same error I made... Pin
Nicholas Butler20-Aug-08 6:03
sitebuilderNicholas Butler20-Aug-08 6:03 
GeneralRe: You made the same error I made... Pin
Paulo Morgado20-Aug-08 13:13
professionalPaulo Morgado20-Aug-08 13:13 
GeneralRe: You made the same error I made... Pin
Nicholas Butler20-Aug-08 23:50
sitebuilderNicholas Butler20-Aug-08 23:50 
GeneralNeed a better example Pin
MR_SAM_PIPER18-Aug-08 16:40
MR_SAM_PIPER18-Aug-08 16:40 
GeneralRe: Need a better example Pin
Nicholas Butler18-Aug-08 23:58
sitebuilderNicholas Butler18-Aug-08 23:58 
GeneralVery similar to what I did with my Dynamic library. Pin
Marc Brooks18-Aug-08 16:17
Marc Brooks18-Aug-08 16:17 
I like what you've done... using delegates really cleans up what I did in the Dynamic library.

http://musingmarc.blogspot.com

GeneralRe: Very similar to what I did with my Dynamic library. Pin
Nicholas Butler19-Aug-08 0:06
sitebuilderNicholas Butler19-Aug-08 0:06 
Generalmight be a trend of doing reflection Pin
RednaxelaFX11-Aug-08 5:10
RednaxelaFX11-Aug-08 5:10 
GeneralRe: might be a trend of doing reflection Pin
Nicholas Butler11-Aug-08 6:13
sitebuilderNicholas Butler11-Aug-08 6:13 
GeneralRe: might be a trend of doing reflection Pin
Roger Alsing13-Aug-08 3:23
Roger Alsing13-Aug-08 3:23 
AnswerRe: might be a trend of doing reflection Pin
Nicholas Butler13-Aug-08 6:00
sitebuilderNicholas Butler13-Aug-08 6:00 

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.