views:

83

answers:

2

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?

+1  A: 
  • MemberAccess : foo => foo.SomeField or foo => foo.SomeProperty
  • Call : foo => foo.SomeMethod(...)
  • Parameter : foo => foo
  • Convert : foo => (int)foo.Something (perhaps implicit)
Marc Gravell
thank you, that's exactly what I wanted to know. I assume that's an exhaustive list? I never even thought of using this for a parameter name, I assume that only works for local variable parameters and there's no way to get the name of another method's parameter name?
Maslow
@Maslow; if you look, it doesn't even return the parameter name (it returns ""). You *could* obtain the "foo" from the `ParameterExpression`, or you could obtain a method'd parameter name by mapping it to the `MethodInfo` and using `GetParameters()`.
Marc Gravell
`MethodInfo` + `GetParameters()` would be actual reflection with the performance hit not static reflection with little/no performance issues right?
Maslow
*all* of this is actual reflection... `Expression` is still built around the reflection API. The only minor tweak is that the C# compiler can refer to `MethodInfo` directly rather than using `GetMethod()`.
Marc Gravell
hmm, I was under the impression that it didn't have the performance hit that some of the reflection concepts had?
Maslow
Once *compiled* (`.Compile()`) the delegate is very fast (much faster than reflection), but the construction is much the same. The slow bit of reflection is things like `GetValue` / `Invoke`, which isn't done during construction - only in use. So from *that* angle a (compiled) `Expression` is much faster than raw reflection.
Marc Gravell
+2  A: 

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.

Jay