What are the use cases for this static reflection code?

287 views Asked by At

This is Oliver Hanappi's static reflection code he posted on stackoverflow

private static string GetMemberName(Expression expression)
    {
        switch (expression.NodeType)
        {
            case ExpressionType.MemberAccess:
                var memberExpression = (MemberExpression)expression;
                var supername = GetMemberName(memberExpression.Expression);

                if (String.IsNullOrEmpty(supername))
                    return memberExpression.Member.Name;

                return String.Concat(supername, '.', memberExpression.Member.Name);

            case ExpressionType.Call:
                var callExpression = (MethodCallExpression)expression;
                return callExpression.Method.Name;

            case ExpressionType.Convert:
                var unaryExpression = (UnaryExpression)expression;
                return GetMemberName(unaryExpression.Operand);

            case ExpressionType.Parameter:
                return String.Empty;

            default:
                throw new ArgumentException("The expression is not a member access or method call expression");
        }
    }

I have the public wrapper methods:

public static string Name<T>(Expression<Action<T>> expression)
    {
        return GetMemberName(expression.Body);
    }
public static string Name<T>(Expression<Func<T, object>> expression)
    {
        return GetMemberName(expression.Body);
    }

then added my own method shortcuts

        public static string ClassMemberName<T>(this T sourceType,Expression<Func<T,object>> expression)
    {
        return GetMemberName(expression.Body);
    }
    public static string TMemberName<T>(this IEnumerable<T> sourceList, Expression<Func<T,object>> expression)
    {
        return GetMemberName(expression.Body);
    }

What are examples of code that would necessitate or take advantage of the different branches in the GetMemberName(Expression expression) switch? what all is this code capable of making strongly typed?

2

There are 2 answers

6
Marc Gravell On BEST ANSWER
  • MemberAccess : foo => foo.SomeField or foo => foo.SomeProperty
  • Call : foo => foo.SomeMethod(...)
  • Parameter : foo => foo
  • Convert : foo => (int)foo.Something (perhaps implicit)
0
Jay On

Many things that require you to pass magic numbers (a generic term that encompasses what people sometimes refer to as "magic strings") can make use of expressions instead to provide type safety.

A common example is in the implementation of the INotifyPropertyChanged interface.

Typically, your property setters include a call like the following:

string _name
public string name
{
    get { return _name; }
    set
    {
        if(value.Equals(_name)) return;
        _name = value;
        OnPropertyChanged("name");
    }
}

Here you are passing the string "name" to identify the property that was changed. This gets nasty when your team lead says "Make all public properties start with a capital letter... and prefix them with the class name." Now you change your property to PersonName, but what is the likelihood you remember to change "name" to "PersonName"? Not high, especially if you didn't write the code originally. Still, the project will compile and you'll spend 20 minutes debugging.

Instead, you use expressions:

string _name
public string name
{
    get { return _name; }
    set
    {
        if(value.Equals(_name)) return;
        _name = value;
        OnPropertyChanged(x => x.name);
    }
}

...and your OnPropertyChanged implementation uses the code you posted to get the name of the property out of the expression body.

Now when you change the property to PersonName, the code won't compile until you also change the expression to read x => x.PersonName. This is your type-safety.

The code apparently uses the switch because the expression may contain nodes of any type; it is not necessarily a MemberExpression to access a property -- it could refer to a method call, the parameter of a method, etc.

That isn't all necessary if you are just implementing INotifyPropertyChanged, but maybe you are also using it to validate parameters or something; the switch just covers the bases for any member access expression, and throws if you give it anything else.