23

The Short Version (TL;DR):

Suppose I have an expression that's just a chain of member access operators:

Expression<Func<Tx, Tbaz>> e = x => x.foo.bar.baz;

You can think of this expression as a composition of sub-expressions, each comprising one member-access operation:

Expression<Func<Tx, Tfoo>>   e1 = (Tx x) => x.foo;
Expression<Func<Tfoo, Tbar>> e2 = (Tfoo foo) => foo.bar;
Expression<Func<Tbar, Tbaz>> e3 = (Tbar bar) => bar.baz;

What I want to do is break e down into these component sub-expressions so I can work with them individually.

The Even Shorter Version:

If I have the expression x => x.foo.bar, I already know how to break off x => x.foo. How can I pull out the other sub-expression, foo => foo.bar?

Why I'm Doing This:

I'm trying to simulate "lifting" the member access operator in C#, like CoffeeScript's existential access operator ?.. Eric Lippert has stated that a similar operator was considered for C#, but there was no budget to implement it.

If such an operator existed in C#, you could do something like this:

value = target?.foo?.bar?.baz;

If any part of the target.foo.bar.baz chain turned out to be null, then this whole thing would evaluate to null, thus avoiding a NullReferenceException.

I want a Lift extension method that can simulate this sort of thing:

value = target.Lift(x => x.foo.bar.baz); //returns target.foo.bar.baz or null

What I've Tried:

I've got something that compiles, and it sort of works. However, it's incomplete because I only know how to keep the left side of a member access expression. I can turn x => x.foo.bar.baz into x => x.foo.bar, but I don't know how to keep bar => bar.baz.

So it ends up doing something like this (pseudocode):

return (x => x)(target) == null ? null
       : (x => x.foo)(target) == null ? null
       : (x => x.foo.bar)(target) == null ? null
       : (x => x.foo.bar.baz)(target);

This means that the leftmost steps in the expression get evaluated over and over again. Maybe not a big deal if they're just properties on POCO objects, but turn them into method calls and the inefficiency (and potential side effects) become a lot more obvious:

//still pseudocode
return (x => x())(target) == null ? null
       : (x => x().foo())(target) == null ? null
       : (x => x().foo().bar())(target) == null ? null
       : (x => x().foo().bar().baz())(target);

The Code:

static TResult Lift<T, TResult>(this T target, Expression<Func<T, TResult>> exp)
    where TResult : class
{
    //omitted: if target can be null && target == null, just return null

    var memberExpression = exp.Body as MemberExpression;
    if (memberExpression != null)
    {
        //if memberExpression is {x.foo.bar}, then innerExpression is {x.foo}
        var innerExpression = memberExpression.Expression;
        var innerLambda = Expression.Lambda<Func<T, object>>(
                              innerExpression, 
                              exp.Parameters
                          );  

        if (target.Lift(innerLambda) == null)
        {
            return null;
        }
        else
        {
            ////This is the part I'm stuck on. Possible pseudocode:
            //var member = memberExpression.Member;              
            //return GetValueOfMember(target.Lift(innerLambda), member);
        }
    }

    //For now, I'm stuck with this:
    return exp.Compile()(target);
}

This was loosely inspired by this answer.


Alternatives to a Lift Method, and Why I Can't Use Them:

The Maybe monad

value = x.ToMaybe()
         .Bind(y => y.foo)
         .Bind(f => f.bar)
         .Bind(b => b.baz)
         .Value;
Pros:
  1. Uses an existing pattern that's popular in functional programming
  2. Has other uses besides lifted member access
Cons:
  1. It's too verbose. I don't want a massive chain of function calls every time I want to drill a few members down. Even if I implement SelectMany and use the query syntax, IMHO that will look more messy, not less.
  2. I have to manually rewrite x.foo.bar.baz as its individual components, which means I have to know what they are at compile time. I can't just use an expression from a variable like result = Lift(expr, obj);.
  3. Not really designed for what I'm trying to do, and doesn't feel like a perfect fit.

ExpressionVisitor

I modified Ian Griffith's LiftMemberAccessToNull method into a generic extension method that can be used as I've described. The code is too long to include here, but I'll post a Gist if anyone's interested.

Pros:
  1. Follows the result = target.Lift(x => x.foo.bar.baz) syntax
  2. Works great if every step in the chain returns a reference type or a non-nullable value type
Cons:
  1. It chokes if any member in the chain is a nullable value type, which really limits its usefulness to me. I need it to work for Nullable<DateTime> members.

Try/catch

try 
{ 
    value = x.foo.bar.baz; 
}
catch (NullReferenceException ex) 
{ 
    value = null; 
}

This is the most obvious way, and it's what I'll use if I can't find a more elegant way.

Pros:
  1. It's simple.
  2. It's obvious what the code is for.
  3. I don't have to worry about edge cases.
Cons:
  1. It's ugly and verbose
  2. The try/catch block is a nontrivial* performance hit
  3. It's a statement block, so I can't make it emit an expression tree for LINQ
  4. It feels like admitting defeat

I'm not going to lie; "not admitting defeat" is the main reason I'm being so stubborn. My instincts say there must be an elegant way to do this, but finding it has been a challenge. I can't believe it's so easy to access the left side of an expression, yet the right side is nigh-unreachable.

I really have two problems here, so I'll accept anything that solves either one:

  • Expression decomposition that preserves both sides, has reasonable performance, and works on any type
  • Null-propagating member access

Update:

Null-propagating member access is planned for included in C# 6.0. I'd still like a solution to expression decomposition, though.

Community
  • 1
  • 1
Justin Morgan - On strike
  • 30,035
  • 12
  • 80
  • 104
  • 1
    Have you considered using [Roslyn](http://blogs.msdn.com/b/csharpfaq/archive/2011/10/19/introducing-the-microsoft-roslyn-ctp.aspx) to rewrite a ?. in your code to the equivalent in the more verbose syntax? It'd become syntactic sugar, and not super performant, but it might do what you want. This is a fascinating question; I've got a couple of ideas I want to try out before committing to an answer. Hopefully someone already has a working, bug-free implementation. – GGulati Jun 19 '12 at 20:03
  • It sounds to me like you're pursuing this for idiomatic purposes -- i.e. you want to eliminate the ugliness of NREs from your code. If so, keep in mind that your code will be leaps and bounds less efficient than it would be otherwise. Expression trees and compiling are things to be used judiciously. But I fear your intent is to use them ubiquitously. – Kirk Woll Jun 19 '12 at 20:03
  • 1
    @GGulati -- if you use that approach, say Good Bye to tooling support. ("bye Resharper, farewell refactoring; I loved you so") – Kirk Woll Jun 19 '12 at 20:05
  • 2
    @KirkWoll - I want it for three reasons: 1) idiomatic purposes, like you say, 2) to explore whether performance will be better or worse, and 3) because I can't figure it out. My most immediate use would be syntactic sugar for nullable value types. Paraphrased from my code: `startDate.Lift(d => d.Value.GetWeek().StartDate.ToString())` would print the first day of the week, or null. – Justin Morgan - On strike Jun 19 '12 at 20:17
  • 1
    Quite true. But anything aside from a concise operator would either have terrible performance (reflection or Expression trees) or be very verbose (his examples, including the ones using Expression trees). I merely want to present an option and explain the trade-offs involved. But, yes, if he plans to use these everywhere his performance will tank dramatically. – GGulati Jun 19 '12 at 20:17
  • @GGulati: you don't necessarily have to ditch that idea. Why not provide a dummy Lift extension that gets the typing correct, then use Roslyn to find and replace that with equivalent syntax for the given situation? *(that last bit is the hard part)* – user7116 Jun 20 '12 at 16:34
  • Maybe my answer here will help you: http://stackoverflow.com/a/5745943/412770 – Ani Jun 21 '12 at 16:33
  • This is now implemented in the latest C# 6 release. – David Mårtensson Sep 01 '15 at 18:33
  • Note that this looks like a duplicate of [this question](http://stackoverflow.com/questions/30488022/how-to-use-expression-tree-to-safely-access-path-of-nullable-objects/30489160#30489160). That answer might help you out. – Servy Dec 04 '15 at 00:46
  • @Servy - Great answer, thanks for drawing my attention to it. I eventually used an approach very similar to yours (which I may, in fact, have gotten from you). I was never able to find a solution that didn't choke on some permutation of value types, nullable value types, and reference types in the same chain, although IIRC I did solve it for each of those groups independently. Maybe your answer will fill in the gaps. – Justin Morgan - On strike Dec 04 '15 at 15:39
  • BTW, I think that question is technically a dupe of this one. – Justin Morgan - On strike Dec 04 '15 at 15:39
  • @JustinMorgan I thought it was, but your question is so long I gave up reading it half way through, and so didn't want to vote to close and copped out with a comment. If there's a case my answer is missing (note that I mention in my answer that there's one case that has no proper solution, and that's where you end on a non-nullable type) comment on it and I'll see if I can fix it. – Servy Dec 04 '15 at 15:41
  • @Servy - I believe that was the exact case I gave up on. IIRC, the only reason it couldn't be resolved was that C# doesn't allow method overloading based on generic type constraints. I understand why they don't, but it was frustrating to be stopped there. – Justin Morgan - On strike Dec 04 '15 at 16:47

1 Answers1

8

If it's just a simple chain of member access expressions, there is an easy solution:

public static TResult Lift<T, TResult>(this T target, Expression<Func<T, TResult>> exp)
    where TResult : class
{
    return (TResult) GetValueOfExpression(target, exp.Body);
}

private static object GetValueOfExpression<T>(T target, Expression exp)
{
    if (exp.NodeType == ExpressionType.Parameter)
    {
        return target;
    }
    else if (exp.NodeType == ExpressionType.MemberAccess)
    {
        var memberExpression = (MemberExpression) exp;
        var parentValue = GetValueOfExpression(target, memberExpression.Expression);

        if (parentValue == null)
        {
            return null;
        }
        else
        {
            if (memberExpression.Member is PropertyInfo)
                return ((PropertyInfo) memberExpression.Member).GetValue(parentValue, null);
            else
                return ((FieldInfo) memberExpression.Member).GetValue(parentValue);
        }
    }
    else
    {
        throw new ArgumentException("The expression must contain only member access calls.", "exp");
    }
}

EDIT

If you want to add support for method calls, use this updated method:

private static object GetValueOfExpression<T>(T target, Expression exp)
{
    if (exp == null)
    {
        return null;
    }
    else if (exp.NodeType == ExpressionType.Parameter)
    {
        return target;
    }
    else if (exp.NodeType == ExpressionType.Constant)
    {
        return ((ConstantExpression) exp).Value;
    }
    else if (exp.NodeType == ExpressionType.Lambda)
    {
        return exp;
    }
    else if (exp.NodeType == ExpressionType.MemberAccess)
    {
        var memberExpression = (MemberExpression) exp;
        var parentValue = GetValueOfExpression(target, memberExpression.Expression);

        if (parentValue == null)
        {
            return null;
        }
        else
        {
            if (memberExpression.Member is PropertyInfo)
                return ((PropertyInfo) memberExpression.Member).GetValue(parentValue, null);
            else
                return ((FieldInfo) memberExpression.Member).GetValue(parentValue);
        }
    }
    else if (exp.NodeType == ExpressionType.Call)
    {
        var methodCallExpression = (MethodCallExpression) exp;
        var parentValue = GetValueOfExpression(target, methodCallExpression.Object);

        if (parentValue == null && !methodCallExpression.Method.IsStatic)
        {
            return null;
        }
        else
        {
            var arguments = methodCallExpression.Arguments.Select(a => GetValueOfExpression(target, a)).ToArray();

            // Required for comverting expression parameters to delegate calls
            var parameters = methodCallExpression.Method.GetParameters();
            for (int i = 0; i < parameters.Length; i++)
            {
                if (typeof(Delegate).IsAssignableFrom(parameters[i].ParameterType))
                {
                    arguments[i] = ((LambdaExpression) arguments[i]).Compile();
                }
            }

            if (arguments.Length > 0 && arguments[0] == null && methodCallExpression.Method.IsStatic &&
                methodCallExpression.Method.IsDefined(typeof(ExtensionAttribute), false)) // extension method
            {
                return null;
            }
            else
            {
                return methodCallExpression.Method.Invoke(parentValue, arguments);
            }
        }
    }
    else
    {
        throw new ArgumentException(
            string.Format("Expression type '{0}' is invalid for member invoking.", exp.NodeType));
    }
}
Balazs Tihanyi
  • 6,659
  • 5
  • 23
  • 24
  • This is great stuff. Two questions: a) Can this be made to emit an expression tree instead, for LINQ methods? b) What about some of the [other types of MemberInfo](http://msdn.microsoft.com/en-us/library/system.reflection.membertypes.aspx)? Some of them might not be relevant to instances, but I'm not sure property/field/method covers everything. – Justin Morgan - On strike Jun 25 '12 at 19:33
  • Hi! a) I think so. You just have to make a long expression tree with conditional operators like (a == null ? null : (a.b == null ? null : a.b.c)). b) I don't think you can create an expression using an event, while the other MemberInfos are not for member access. – Balazs Tihanyi Jun 25 '12 at 21:31
  • 1
    Thanks for this. I was hoping for something more...I don't know, elegant? But maybe there really isn't one until a `.?` operator makes its way into C#. Out of curiosity, did you have a chance to check out the link to [Ian Griffith's LiftMemberAccessToNull](http://www.interact-sw.co.uk/iangblog/2008/04/13/member-lifting), which uses an ExpressionVisitor? The only problem with that is that it chokes on `System.Nullable<>` instances. If you have any insight into that, I'd create another question for a workaround. – Justin Morgan - On strike Jun 26 '12 at 16:34