IT Hit WebDAV
IT Hit WebDAV

Reputation: 5894

Null-conditional operator analog in .NET 3.5 / 4.0, VS 2012 / 2013?

In my code I have to access values calling several property getters:

IFoo1 a = objA.Prop1.Value;
IFoo2 b = objB.Prop2.Prop3.Value;
IFoo3 c = objC.Prop4.Prop5.Prop6.Value;

Every property can be null. So to access every value I must use nested if-blocks:

IFoo2 b = null;

if(objB.Prop2!=null)
{
    if(objB.Prop2.Prop3!=null)
    {
         b = objB.Prop2.Prop3.Value;
    }
}

How can I improve this code to reduce amount of if-blocks? Can I use any lambda expressions, LINQ, IExpression, etc, to somehow replace it with:

IFoo2 b = GetVal(objB.Prop2.Prop3.Value);

All PropX are of different types and I have hundreds of such properties. I must use .NET 3.5 or at least .NET 4.0. I can not use any later versions.

IMPORTANT EDIT:

I must also use Visual Studio 2012 and 2013. I can not target VS 2015.

Upvotes: 1

Views: 2724

Answers (4)

Rob
Rob

Reputation: 27367

This will do it, but it's very very quick and dirty. Some obvious flaws:

  1. For the expression a.b.c - we invoke 'a', then 'a.b', then 'a.b.c', checking for null each time. We should store the return of the previous invocation, and modify the member expression to operate on our result. Only really an issue if the member access is expensive, otherwise it's equivalent to if (a != null && a.b != null && a.b.c != null) return a.b.c.d; which is a fairly common pattern
  2. It only works for member expressions

public static T GetOrNull<T>(Expression<Func<T>> expression) 
    where T : class
{

    var memberExpressions = new List<MemberExpression>();
    var membExpress = expression.Body as MemberExpression;
    while (membExpress != null)
    {
        memberExpressions.Add(membExpress);
        membExpress = membExpress.Expression as MemberExpression;
    }
    memberExpressions.Skip(1).Reverse();

    foreach(var membExpr in memberExpressions.Skip(1).Reverse()) {
        var lambdaExpr = Expression.Lambda(membExpr);
        var currentRes = lambdaExpr.Compile().DynamicInvoke();
        if (currentRes == null)
            return null;
    }   

    return (T)Expression.Lambda(expression.Body).Compile().DynamicInvoke();
}

And use it like this:

var tmp = new classA();

var res = GetOrNull(() => tmp.Prop1.Prop2);
res.Dump(); //Gives null

tmp.Prop1 = new classA.classB();
tmp.Prop1.Prop2 = new classA.classB.classC();
res = GetOrNull(() => tmp.Prop1.Prop2);
res.Dump(); //returns object of type `classC`

Upvotes: 1

Thomas Levesque
Thomas Levesque

Reputation: 292475

Unfortunately you need C# 6 to use the null-conditional operator (?.).

However, you can simulate it with extension methods like these:

static class Extensions
{
    public static TReturn NCR<T, TReturn>(this T instance, Func<T, TReturn> getter)
        where T : class
        where TReturn : class
    {
        if (instance != null)
            return getter(instance);
        return null;
    }

    public static TReturn NCR<T, TReturn>(this T? instance, Func<T, TReturn> getter)
        where T : struct
        where TReturn : class
    {
        if (instance != null)
            return getter(instance.Value);
        return null;
    }

    public static TReturn? NCV<T, TReturn>(this T instance, Func<T, TReturn> getter)
        where T : class
        where TReturn : struct
    {
        if (instance != null)
            return getter(instance);
        return null;
    }

    public static TReturn? NCV<T, TReturn>(this T? instance, Func<T, TReturn> getter)
    where T : struct
    where TReturn : struct
    {
        if (instance != null)
            return getter(instance.Value);
        return null;
    }
}

(NC stands for Null-Conditional, R stands for Reference type, V stands for Value type; it's ugly, but unfortunately C# doesn't allow method overloads that differ only by the generic constraints)

You can use them like this:

IFoo3 c = objC.NCR(_ => _.Prop4)
              .NCR(_ => _.Prop5)
              .NCR(_ => _.Prop6)
              .NCR(_ => _.Value);

(use NCV instead of NCR if the property to get returns a value type)

It's still too verbose, but at least it's easier to see what the code is doing.

Upvotes: 2

Rahul
Rahul

Reputation: 77886

Best you can probably do is using a compound if condition like

IFoo2 b = null;

if(objB.Prop2 != null && objB.Prop2.Prop3 != null)
{ 
  b = objB.Prop2.Prop3.Value;
}

(OR) using a Ternary Operator like

IFoo2 b = (objB.Prop2 != null && objB.Prop2.Prop3 != null) ? objB.Prop2.Prop3.Value : null;

Upvotes: 0

Etienne Charland
Etienne Charland

Reputation: 4024

.NET 4.6 now has a ?. operator that solves this exact problem.

http://www.volatileread.com/Wiki?id=2104

This allows you to write it like this.

IFoo3 c = objC?.Prop4?.Prop5?.Prop6?.Value;

You might get around this by creating a helper function, but the clarity and simplicity of the code won't get anywhere near this. If possible, upgrade to get that feature.

Upvotes: -1

Related Questions