0

With C# 6 we can use the new ? to access properties and methods without heaving to check each one for null. Would it be possible to write a method that has a siliar functionality using expressions?

For example I have to use a weird object structures (that comes from a 3rd party library that we cannot change). Accessing some properties requires often long chains of dots:

rootObject.Services.First().Segments.First().AnotherCollection.First().Weight;

Any of the objects after rootObject can be null. I would rather not put a try/catch around it. Also checking each property separately is a lot of work. So I was wondering if I could pass it to the method taking an expression and traverse each property and check its value there:

var value = PropertyHelper.GetValue(() => rootObject.Services.First().Segments.First().AnotherCollection.First().Weight);

I guess its signature would be something like:

public static T GetValue<T>(Expression<Func<T>> expression)
{
    // analyze the expression and evaluate each property/method
    // stop when null or return the value
}

I'm not realy sure if expressions are capable of what I'm going to do and before I start experimenting I wanted to ask whether it's even possible.

t3chb0t
  • 16,340
  • 13
  • 78
  • 118
  • There's a reason that they added it as an operator. It's *possible* but it's a lot clunkier to use than the C# 6 operator. There's also issues in dealing with non-nullable types that the C# operator is capable of solving (and that you can't ever solve). – Servy Jan 06 '16 at 16:51
  • Well, you could create (subclass) an `ExpressionVisitor` that checks for member-access, invoke, etc, and injects (into the rewritten expression) the checks, but.. that's a lot of reflection and a lot of visiting, and then you either need to `Compile()` it (adding meta-programming) or add *even more* reflection to evaluate the tree at runtime... seems a lot of overhead – Marc Gravell Jan 06 '16 at 16:52
  • @MarcGravell True, although if the code in question isn't in a particularly performance sensitive section of the application, and is either not called frequently, or the compiled delegate can be reused, then performance may not be bad enough to rule out the solution. – Servy Jan 06 '16 at 16:55
  • @Servy the other question pretty much answers mine too I think and I didn't know how to formulate it differently, that's why I probably didn't find it. I'll study it and try something too. Thank you. – t3chb0t Jan 06 '16 at 16:56
  • @MarcGravell if it's doable then I'll get to work ;-) I use the other question for inspiration. – t3chb0t Jan 06 '16 at 16:57

1 Answers1

2
void Main()
{
    var foo = new Foo();

    var qux = NullPropertyExtension.GetValue(() => foo.Bar.Qux);

    Console.WriteLine(qux);


}

public class Foo
{
    public Foo Bar { get; set; }
    public string Qux {get;set;}
}

// Define other methods and classes here
public static class NullPropertyExtension
{
    public static TValue GetValue<TValue>(Expression<Func<TValue>> property)
    {
        var visitor = new Visitor();
        var expression = visitor.Visit(property.Body);
        var lambda = Expression.Lambda<Func<TValue>>(expression);

        var func = lambda.Compile();
        return func();
    }

    private class Visitor : System.Linq.Expressions.ExpressionVisitor
    {
        protected override Expression VisitMember(MemberExpression node)
        {
            var isNotNull = Expression.NotEqual(node.Expression, Expression.Constant(null));
            return Expression.Condition(
                isNotNull,
                node,
                Expression.Constant(null, node.Type));

        }
    }
}
Aron
  • 15,464
  • 3
  • 31
  • 64
  • Wow, so _simple_ or rather short ;-) but still magic to me. I need to study it. Thank you. – t3chb0t Jan 06 '16 at 17:11
  • @t3chb0t Note this class ONLY deals with nulls on an object where you are trying to get a property/field from. For `.First()` to work you need to override the `VisitCallMethod` method (being careful of static methods). – Aron Jan 06 '16 at 17:19
  • Or even methods where `null` as a parameter is ligit. – Aron Jan 06 '16 at 17:20
  • Great, thx for the hint. I will need some time to understand it completely but it's a good start if you have something you can work with :-) I'm also planning to _record_ the path to be able to log it later so that I know where it _failed_ (where the null was). This is not possible with C# 6 so I will definitely stick to this solution. – t3chb0t Jan 06 '16 at 17:23
  • 1
    @t3chb0t I recommend you download LinqPad to view to expression tree at each step. It really helped with my learning Expression Tree metaprogramming. – Aron Jan 06 '16 at 17:25
  • I even have bought it some time ago ;-) and I was always wondering what the `Tree` button does... now I see it wonderfully shows the entire tree. Another great hint. I would give you another upvote if I could. – t3chb0t Jan 06 '16 at 17:29