Click here to Skip to main content
15,883,817 members
Articles / Programming Languages / C#

Getting Fun with .NET Static Reflection

Rate me:
Please Sign up or sign in to vote.
4.00/5 (9 votes)
2 Jul 2009CPOL4 min read 39.1K   33   11
How the usage of Static Reflection could help in the refactoring process

Hi there to everyone. Today I want to talk about a fantastic technique that I think it will help you a lot in your daily programming tasks. This technique is called Static Reflection, here is a nice explanation about what it does:

Static Reflection gathers meta information through the inspection of the Expression Tree.

Thanks to this, you do not need any magic string to interrogate a given type for their properties or methods (like Dynamic Reflection happens).

One of the benefits that you could get from this technique is to create highly “refactorable” (is that a word?) projects due to the elimination of magic strings.

Let me show you an example about this scenario:

Let's say that we have a junior programmer who does have a very bad way of naming things in code, he likes to use TXTSpeak a lot while writing code.
So one day we ask him to write a tiny Windows app which should load in a dropdown a list of persons, and he goes ahead and writes the following data structure:

C#
public class PRSN //"Person" = PRSN in TXTSpeak
{
    public int PRSNID { get; set; } //"PersonID" = PRSNID in TXTSpeak
    public string PRSNNM { get; set; } //"PersonName" = PRSNNM in TXTSpeak
}   

And his winform client would look like:

C#
public partial class Form1 : Form
{
    public Form1()
    {
        fillList();
        InitializeComponent();
    }
 
    private List<PRSN> dropdownDataSource = new List<PRSN>();
 
    private void fillList()
    {
        // in the real world here you should call to the database 
        // to retrieve this data
        // for the sake of the demo just use the following
 
        dropdownDataSource.Add(new PRSN()
        {
            PRSNID = 1,
            PRSNNM = "Jaime"
        });
        dropdownDataSource.Add(new PRSN()
        {
            PRSNID = 2,
            PRSNNM = "John"
        });
 
        dropdownDataSource.Add(new PRSN()
        {
            PRSNID = 3,
            PRSNNM = "Charles"
        });
 
        dropdownDataSource.Add(new PRSN()
        {
            PRSNID = 3,
            PRSNNM = "Mark"
        });
    }
 
    private void Form1_Load(object sender, EventArgs e)
    {
        cboPersons.ValueMember = "PRSNID";
        cboPersons.DisplayMember = "PRSNNM";
        cboPersons.DataSource = dropdownDataSource;
    }
}  

Here is the thing, please take a look at how the ValueMember and DisplayMember properties are being configured, the original programmer used just a simple string to point to the name of the properties in his data structure. Now, your manager assigns you a task to clean up this mess and replace all that TXTSpeak with proper names. What is worse is that this kind of code is all over the place and you would have to go with the painful process of “search/replace” all over your code.

Is there a better way? Sure there is, here is where Static Reflection comes in.

With Static Reflection, all you have to do is to pass an Expression indicating which property you want to inspect and that will give you a PropertyInfo object from where you can ask for the Name property of such instance and do the very same thing… but better :).

First of all, let me explain how this works. In the project attached to this article, you'll find the following structure:

The project where all the interesting stuff happens is the EmiajNet.StaticReflection.Library, in there you are going to find the ReflectionHelper and StaticReflector static classes. Both of them have methods related to static reflection, but why two? Well, the ReflectionHelper class has been borrowed from another library (FluentNHibernate) so I think it is better to have it in a separated class, and the StaticReflector class has been created by me to fill some functionality that the ReflectionHelper class does not provide (static reflection over methods that return void).

Here is a class diagram to illustrate how those classes look like:

The basic idea of all of them is to inspect the expression being passed and extract the object that is related to the signature of the method. So the GetMethod method returns a MethodInfo, the GetProperty method returns a PropertyInfo and so on.

This is how some of those methods look like:

ReflectionHelper

C#
public static MethodInfo GetMethod<T>(Expression<Func<T, object>> expression)
{
    MethodCallExpression methodCall = (MethodCallExpression) expression.Body;
    return methodCall.Method;
}

public static PropertyInfo GetProperty<MODEL>(Expression<Func<MODEL, object>> expression) 
{ 
    return (PropertyInfo) GetMember(expression);
} 

public static MemberInfo GetMember<MODEL>(Expression<Func<MODEL, object>> expression)
{ 
    MemberExpression memberExpression = getMemberExpression(expression);
    return memberExpression.Member; 
} 

public static FieldInfo GetField<MODEL>(Expression<Func<MODEL, object>> expression)
{ 
    return (FieldInfo)GetMember(expression);
} 

StaticReflector

C#
public static MethodInfo GetMethod<MODEL>(Expression<Func<MODEL, Action>> exp)
{
    return GetMethodFromLambda(exp);
}

private static MethodInfo GetMethodFromLambda(LambdaExpression exp)
{
    var unaryExp = (UnaryExpression)exp.Body;
    
    var methodCallExp = (MethodCallExpression)unaryExp.Operand;
    
    var constantExp = (ConstantExpression)methodCallExp.Arguments[2];
    
    MethodInfo output = (MethodInfo)constantExp.Value;
    
    return output;
}

As you can see, this is very straightforward. You can dig more into the library to give a more detailed idea.

Here are some examples from the EmiajNet.StaticReflection.Test project that I want to show you so you could realize how this works:

We have the following class that is the subject of inspection:

C#
private class TestClass
{
    public int SomeIntMember;
    public string SomeStringMember;
    public TestClass SomeComplexMember;
    public string SomeStringProperty { get; set; }
    public int SomeIntProperty { get; set; }
    
    //indexed property
    public object this[int index]
    {
        get
        {
            return null;
        }
    }
    
    public TestClass SomeComplexProperty { get; set; }
    
    public void SomeParameterlessMethod()
    {
    }
    public void SomeMethod(object x)
    {
    }
    public TestClass SomeParameterlessMethodWithReturnValue()
    {
        return null;
    }
    
    public TestClass SomeMethodWithReturnValue(object x)
    {
        return null;
    }
}

And here are some tests that I throw at it:

C#
[Fact()] 
public void TestStringProperty()
{
    string expected = "SomeStringProperty";
    string propertyName = ReflectionHelper.GetProperty<TestClass>
			(x => x.SomeStringProperty).Name;
    Assert.Equal(expected, propertyName);
}

[Fact()]
public void TestFunction()
{
    string expected = "SomeMethodWithReturnValue";
    string methodName = ReflectionHelper.GetMethod<TestClass>
			(x => x.SomeMethodWithReturnValue(null)).Name;
    Assert.Equal(expected, methodName);
}

[Fact()] 
public void TestMethod()
{
    string expected = "SomeMethod";
    string methodName = StaticReflector.GetMethod<TestClass, 
			object>(x => x.SomeMethod).Name;
    Assert.Equal(expected, methodName);
} 

As you can see, those tests verify that the name of the expressions being evaluated are correct. Remember all of this is only to obtain information about the expression being inspected.

Now, continuing with the scenario that we talk about earlier, here is how this sample looks like using Static Reflection:

C#
private void Form1_Load(object sender, EventArgs e)
{
    cboPersons.ValueMember = ReflectionHelper.GetProperty<PRSN>(x => x.PRSNID).Name;
    cboPersons.DisplayMember = ReflectionHelper.GetProperty<PRSN>(x => x.PRSNNM).Name;
    cboPersons.DataSource = dropdownDataSource;
} 

Isn’t this something more refactor friendly?, I mean, now you just should go and rename the class and its properties correctly and with the help of Visual Studio every occurrence of the affected properties is going to be changed automatically.

Refactor_Rename_Property

If we accept this action, our code will be updated automatically and the application will continue working as usual:

C#
private void Form1_Load(object sender, EventArgs e)
{
    cboPersons.ValueMember = ReflectionHelper.GetProperty<PRSN>(x => x.PersonID).Name;
    cboPersons.DisplayMember = ReflectionHelper.GetProperty<PRSN>(x => x.PRSNNM).Name;
    cboPersons.DataSource = dropdownDataSource;
} 

Now you could continue refactoring the class name to Person and the other property to PersonName.

You can download the sample code to play around from here. There is a series of unit tests so you could check in more depth how this works. To run them all from inside Visual Studio, you will need to have installed TestDriven .NET and configure XUnit as your test suite.

Hope you enjoyed the article.

Bye bye.

Shameless plug: You can check this article on my blog here.

License

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


Written By
Web Developer
Peru Peru
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralInfoOf Pin
emiaj30-Jun-09 5:51
emiaj30-Jun-09 5:51 
GeneralRe: InfoOf - Bad News Pin
emiaj6-Jul-09 7:15
emiaj6-Jul-09 7:15 
Generali wonder... Pin
conscientious_coder30-Jun-09 3:41
conscientious_coder30-Jun-09 3:41 
GeneralRe: i wonder... Pin
emiaj30-Jun-09 5:44
emiaj30-Jun-09 5:44 
GeneralRe: i wonder... Pin
conscientious_coder30-Jun-09 6:17
conscientious_coder30-Jun-09 6:17 
GeneralRe: i wonder... [modified] Pin
emiaj30-Jun-09 7:49
emiaj30-Jun-09 7:49 
QuestionCould this easily convert identifiers to expanded forms? Pin
supercat924-Jun-09 18:26
supercat924-Jun-09 18:26 
If I have a module that IMPORTs something, coould this approach be used to convert identifiers into "long form", e.g. turn "StringBuilder" into "System.Text.Stringbuilder"?

On a vaguely related note, is there any easy way in vb to define a shorthand name for a class so as to avoid having to type out the fully qualified name all the time, without having to IMPORTs an entire namespace (which could cause naming conflicts elsewhere)?
AnswerRe: Could this easily convert identifiers to expanded forms? [modified] Pin
emiaj25-Jun-09 3:12
emiaj25-Jun-09 3:12 
GeneralRe: Could this easily convert identifiers to expanded forms? Pin
supercat925-Jun-09 5:58
supercat925-Jun-09 5:58 
GeneralRe: Could this easily convert identifiers to expanded forms? Pin
emiaj25-Jun-09 6:36
emiaj25-Jun-09 6:36 
Generali kown you mean but i don't kown what i to do Pin
zzx_12345624-Jun-09 16:44
zzx_12345624-Jun-09 16:44 

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.