Getting property and method names using static reflection in C#

For several years I’ve had a little “utility” function that I’ve used in several projects that I use to convert property names into strings. One use case is for instance when mapping code to some data source or third party API that where the names are used as keys. The method uses “static reflection”, or rather it parses the expression tree from a lambda expression, to figure out the name of a property that the lambda expression returns the value of.

So far I haven’t really needed to do the same for methods or fields but today I decided I would try to make it a bit more generic anyway. In other words, I wanted to be able to do stuff like this:

//Should return "Length", value type property
StaticReflection.GetMemberName<string>(x => x.Length);

//Should return "Data", reference type property
StaticReflection.GetMemberName<Exception>(x => x.Data);

//Should return "Clone", method returning reference type
StaticReflection.GetMemberName<string>(x => x.Clone());

//Should return "GetHashCode", method returning value type
StaticReflection.GetMemberName<string>(x => x.GetHashCode());

//Should return "Reverse", void method
StaticReflection.GetMemberName<List<string>>(x => x.Reverse());

//Should return "LastIndexOf", method with parameter
StaticReflection.GetMemberName<string>(x => x.LastIndexOf(','));

Or, by having an overload that is an extension method achieve the same effect but utilizing type inference so we don’t have to explicitly specify the type if we have an instance of it, for instance like this:

//Should return Length, no type parameter required
"someString".GetMemberName(x => x.Length);

The solution

In order to fulfill the above requirements we need two methods, one accepting a Expression<Func<T, object>> and one, used for methods without return types, an Expression<Action<T>> as parameters. We also need the corresponding extension methods. Once we have those we need to look at the Body property of the expressions and figure out what types of expressions they are. In other words, if we’re dealing with a field, property or method as well as if the return type is a value type or reference type as the way to get the name from the expression tree differs depending on that.

My solution was to create a static class with a couple of private method that did the bulk of the work.

private static string GetMemberName(Expression expression)
{
    if (expression == null)
    {
        throw new ArgumentException(
            "The expression cannot be null.");
    }

    if (expression is MemberExpression)
    {
        // Reference type property or field
        var memberExpression = (MemberExpression) expression;
        return memberExpression.Member.Name;
    }

    if (expression is MethodCallExpression)
    {
        // Reference type method
        var methodCallExpression = 
            (MethodCallExpression) expression;
        return methodCallExpression.Method.Name;
    }

    if (expression is UnaryExpression)
    {
        // Property, field of method returning value type
        var unaryExpression = (UnaryExpression) expression;
        return GetMemberName(unaryExpression);
    }

    throw new ArgumentException("Invalid expression");
}

private static string GetMemberName(
        UnaryExpression unaryExpression)
{
    if (unaryExpression.Operand is MethodCallExpression)
    {
        var methodExpression = 
            (MethodCallExpression) unaryExpression.Operand;
        return methodExpression.Method.Name;
    }
    
    return ((MemberExpression) unaryExpression.Operand)
                .Member.Name;
}

The if statements could be replaced with an “action map” but this form allowed me to better illustrate what the methods, especially the first one, do. Anyhow, with the private methods in place the public methods are pretty straight forward. First the static helper methods:

public static string GetMemberName<T>(Expression<Func<T, object>> expression)
{
    if (expression == null)
    {
        throw new ArgumentException(
            "The expression cannot be null.");
    }

    return GetMemberName(expression.Body);
}

public static string GetMemberName<T>(
        Expression<Action<T>> expression)
{
    if (expression == null)
    {
        throw new ArgumentException(
            "The expression cannot be null.");
    }

    return GetMemberName(expression.Body);
}

And the corresponding extension methods:

public static string GetMemberName<T>(
    this T instance, Expression<Func<T, object>> expression)
{
    return GetMemberName(expression);
}

public static string GetMemberName<T>(
    this T instance, Expression<Action<T>> expression)
{
    return GetMemberName(expression);
}

Note how we’re not actually using the instance the extension methods are called on. They are only there to allow us to invoke the methods on instances of a type directly instead of specifying the type as the compiler can then infer the type parameter (T). In other words, we could in theory do something like this:

((string) null).GetMemberName(x => x.Length);

That seems a bit pointless though as we’re probably better off using the regular methods in to achieve the same result.

The full class looks like this:

public static class StaticReflection
{
    public static string GetMemberName<T>(
        this T instance, 
        Expression<Func<T, object>> expression)
    {
        return GetMemberName(expression);
    }

    public static string GetMemberName<T>(
        Expression<Func<T, object>> expression)
    {
        if (expression == null)
        {
            throw new ArgumentException(
                "The expression cannot be null.");
        }

        return GetMemberName(expression.Body);
    }

    public static string GetMemberName<T>(
        this T instance, 
        Expression<Action<T>> expression)
    {
        return GetMemberName(expression);
    }

    public static string GetMemberName<T>(
        Expression<Action<T>> expression)
    {
        if (expression == null)
        {
            throw new ArgumentException(
                "The expression cannot be null.");
        }

        return GetMemberName(expression.Body);
    }

    private static string GetMemberName(
        Expression expression)
    {
        if (expression == null)
        {
            throw new ArgumentException(
                "The expression cannot be null.");
        }

        if (expression is MemberExpression)
        {
            // Reference type property or field
            var memberExpression = 
                (MemberExpression) expression;
            return memberExpression.Member.Name;
        }

        if (expression is MethodCallExpression)
        {
            // Reference type method
            var methodCallExpression = 
                (MethodCallExpression) expression;
            return methodCallExpression.Method.Name;
        }

        if (expression is UnaryExpression)
        {
            // Property, field of method returning value type
            var unaryExpression = (UnaryExpression) expression;
            return GetMemberName(unaryExpression);
        }

        throw new ArgumentException("Invalid expression");
    }

    private static string GetMemberName(
        UnaryExpression unaryExpression)
    {
        if (unaryExpression.Operand is MethodCallExpression)
        {
            var methodExpression = 
                (MethodCallExpression) unaryExpression.Operand;
            return methodExpression.Method.Name;
        }

        return ((MemberExpression) unaryExpression.Operand)
            .Member.Name;
    }
}

For the really interested person, it passes a number of tests, or executable specifications verified and generated using MSpec, producing the following specification:

StaticReflection GetMemberName, given a Func returning a value type property
» should return the name of the property

StaticReflection GetMemberName, given a Func returning a reference type property
» should return the name of the property

StaticReflection GetMemberName, given a Func invoking a reference type method
» should return the name of the method

StaticReflection GetMemberName, given a Func invoking a value type method
» should return the name of the method

StaticReflection GetMemberName, given a Func invoking a void method
» should return the name of the method

StaticReflection GetMemberName, given a Func invoking a method with a parameter
» should return the name of the method

StaticReflection GetMemberName, called with null cast to Func
» should throw an exception
» should throw an ArgumentException

StaticReflection GetMemberName, called with null cast to Action
» should throw an exception
» should throw an ArgumentException

PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.

Comments

  1. Michael's avatar

    Michael 7 months ago

    Excellent - thank you for taking the time to share this.

  2. Roger Norling's avatar

    Roger Norling 6 months ago

    Nice, I were looking for something like this. I took the opportunity to improve on this though. You can get rid of the object type in the delegates by declaring them e.g. Func<T, TResult> and change the method generic parameters to GetMemberName<T, TResult> . This means the generics are implicit and the lambda parameter type is made explicit instead. With a more fluent naming it might look like this:

    MemberName.For((string x) => x.Length);


    I've also added support for static methods, properties and fields. I skipped the extension methods since I don't find it useful to create new instances just to extract a name. It is still useful to use 'this' inside a method to get it's own name in a refactoring safe way though. I don't have null checks, but I see this as a utility so the arguments should be trusted.

    Here is my code (http://pastebin.com/j0neZ478), with NUnit tests (http://pastebin.com/tu4neNQr).
  3. Shankar's avatar

    Shankar 5 months ago

    I am struggling to find the property name from string , lets say i have sting "AccountName" , i am passing that string to some method, can i get property from that string , i know there is property called AccountName for some model, by just passing string can i get the property.

  4. Michael's avatar

    Michael 5 months ago

    @Shankar: I don't think that's possible: the only type that you can reflect on in your scenario is [string]; where that string came from is no longer available at that point.

  5. Roger Norling's avatar

    Roger Norling 5 months ago

    Shankar: A string is just a string, so if you only have access to the name of the property then you can't do much more. If you have access to an instance on the other hand, you can do this http://pastebin.com/XSYHvssA .

    Note though that all exception checking is skipped for the sake of the example. The less you know about your objects in your code the more you need to check for errors (This includes changes you do due to developing your code).

Follow me on Twitter

  1. @tim_abell The gigantic ones are for customers who specifically ask for them only :) 1 months ago
  2. Looking to buy a gigantic easter egg filled with candy for delivery in Stockholm. Any recommendations? 1 months ago
  3. @strandberg_m Du måste skriva om resultatet efteråt! 1 months ago
follow me

Latest comments

  1. Joel Abrahamsson wrote "Hi Jonas! The fluent API is really geared towards working..." on Building a search page for an EPiServer site using Truffler
  2. Jonas wrote "Thank you for one more great write up! If you're not lucky ..." on Building a search page for an EPiServer site using Truffler
  3. David Knipe wrote "The CategoriesFacet method will save me a load of headaches ..." on Cool new features in the Truffler .NET API

About this site

This blog is built with EPiServer Community, EPiServer CMS, ASP.NET MVC and a bunch of other great products. The source code is available for download at the projects page, where you also can read more about this site and my other projects.

read more