Reflecting on Code

Static reflection. It’s all the rage.

What is static reflection? It’s obtaining type information at compile time instead of runtime, typically by using LINQ expression trees. Here’s my take on providing a static helper to do this.

public static class Reflect
{
    public static MemberInfo GetMember(Expression<Action> expression)
    {
        if (expression == null)
        {
            throw new ArgumentNullException(
                GetMember(() => expression).Name);
        }

        return GetMemberInfo(expression as LambdaExpression);
    }

    public static MemberInfo GetMember<T>(Expression<Func<T>> expression)
    {
        if (expression == null)
        {
            throw new ArgumentNullException(
                GetMember(() => expression).Name);
        }

        return GetMemberInfo(expression as LambdaExpression);
    }

    public static MethodInfo GetMethod(Expression<Action> expression)
    {
        MethodInfo method = GetMember(expression) as MethodInfo;
        if (method == null)
        {
            throw new ArgumentException(
                "Not a method call expression", GetMember(() => expression).Name);
        }

        return method;
    }

    public static PropertyInfo GetProperty<T>(Expression<Func<T>> expression)
    {
        PropertyInfo property = GetMember(expression) as PropertyInfo;
        if (property == null)
        {
            throw new ArgumentException(
                "Not a property expression", GetMember(() => expression).Name);
        }

        return property;
    }

    public static FieldInfo GetField<T>(Expression<Func<T>> expression)
    {
        FieldInfo field = GetMember(expression) as FieldInfo;
        if (field == null)
        {
            throw new ArgumentException(
                "Not a field expression", GetMember(() => expression).Name);
        }

        return field;
    }

    internal static MemberInfo GetMemberInfo(LambdaExpression lambda)
    {
        if (lambda == null)
        {
            throw new ArgumentNullException(
                GetMember(() => lambda).Name);
        }

        MemberExpression memberExpression = null;
        if (lambda.Body.NodeType == ExpressionType.Convert)
        {
            memberExpression = ((UnaryExpression)lambda.Body).Operand as MemberExpression;
        }
        else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
        {
            memberExpression = lambda.Body as MemberExpression;
        }
        else if (lambda.Body.NodeType == ExpressionType.Call)
        {
            return ((MethodCallExpression)lambda.Body).Method;
        }

        if (memberExpression == null)
        {
            throw new ArgumentException(
                "Not a member access", GetMember(() => lambda).Name);
        }

        return memberExpression.Member;
    }
}

If you look closely, the code itself shows you a use for the idea. Yep, it’s definitely dogfood. Pay attention to how we raise ArgumentNullExceptions in this code. No “magic strings” for us. This is compiled code. If I use the wonderful IDE refactoring capabilities to rename the argument, the string passed to the ArgumentNullConstructor is also refactored, because it’s static code and not a string literal.

There’s also situations where you don’t have an instance of data to use for reflecting, and the above code isn’t so useful in this case. That’s where it’s big brother comes in.

public static class ReflectOn<T>
{
    public static MemberInfo GetMember(Expression<Action<T>> expression)
    {
        if (expression == null)
        {
            throw new ArgumentNullException(Reflect.GetMember(() => expression).Name);
        }

        return Reflect.GetMemberInfo(expression as LambdaExpression);
    }

    public static MemberInfo GetMember<TResult>(Expression<Func<T, TResult>> expression)
    {
        if (expression == null)
        {
            throw new ArgumentNullException(Reflect.GetMember(() => expression).Name);
        }

        return Reflect.GetMemberInfo(expression as LambdaExpression);
    }

    public static MethodInfo GetMethod(Expression<Action<T>> expression)
    {
        MethodInfo method = GetMember(expression) as MethodInfo;
        if (method == null)
        {
            throw new ArgumentException(
                "Not a method call expression",
                Reflect.GetMember(() => expression).Name);
        }

        return method;
    }

    public static PropertyInfo GetProperty<TResult>(Expression<Func<T, TResult>> expression)
    {
        PropertyInfo property = GetMember(expression) as PropertyInfo;
        if (property == null)
        {
            throw new ArgumentException(
                "Not a property expression", Reflect.GetMember(() => expression).Name);
        }

        return property;
    }

    public static FieldInfo GetField<TResult>(Expression<Func<T, TResult>> expression)
    {
        FieldInfo field = GetMember(expression) as FieldInfo;
        if (field == null)
        {
            throw new ArgumentException(
                "Not a field expression", Reflect.GetMember(() => expression).Name);
        }

        return field;
    }
}

Now I can do something like this.

class Program
{
    public void Foo()
    {
    }

    static void Main(string[] args)
    {
        Console.WriteLine(ReflectOn<Program>.GetMethod(p => p.Foo()).Name);
    }
}

Want another, more practical use?

public abstract class ObservableObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    protected void OnPropertyChanged() // All properties changed
    {
        OnPropertyChanged(null);
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    protected void OnPropertyChanged<T>(Expression<Func<T>> expression)
    {
        OnPropertyChanged(Reflect.GetProperty(expression).Name);
    }
}

Which is used like this.

public class Foo : ObservableObject
{
    private string message;

    public string Message
    {
        get
        {
            return this.message;
        }

        set
        {
            this.message = value;
            OnPropertyChanged(() => Message);
        }
    }
}

No more magic strings when raising PropertyChanged from INotifyPropertyChanged implementations.

The performance here is obviously worse than using the magic string, but it’s a lot better than using actual runtime reflection.

Add a Comment