Null coalescing within an invocation chain

1.4k views Asked by At

If I have a long list of objects that each has the possibility of returning null within a "Linq where" clause, e.g.

 SomeSource.Where(srcItem=>(srcItem.DataMembers["SomeText"].Connection.ConnectedTo as Type1).Handler.ForceInvocation == true));

the indexer can return null and the "as" operator may return null. It is possible that the object does not have a connection (ie. The property is null). If a null is encountered anywhere, I would like the where clause to return "false" for the item being evaluated. Instead, it aborts with a null reference exception.

It appears to me that this would be contrived to express within a single C# expression. I don't like to create a multi line statement or create a separate func for it. Is there some use of the null coalescing operator that I'm missing?

4

There are 4 answers

4
Dan Tao On BEST ANSWER

You're looking for the .? operator (or is it ?.—one of those, anyway), which does not exist in C# (though it is an often-requested feature, according to Eric Lippert).

The only possible suggestion I have is to write a method that takes an expression and uses it to check for any nulls. But this will come at a performance cost. Anyway, it might look like:

T TryOrDefault<T>(Expression<Func<T>> expression)
{
    // Check every MemberExpression within expression one by one,
    // looking for any nulls along the way.

    // If a null is found, return default(T) or some default value.

    // Otherwise...
    Func<T> func = expression.Compile();
    return func();
}
1
Amy B On

create a separate func for it

This is the way to go. Do not be allergic to proper techniques. Methods you create are no more expensive (at runtime, and conceptually) than anonymous methods.

3
Chris Shouts On

Using the andand operator from Ruby as inspiration, you could create an extension method that acts as a null guard.

public static U AndAnd<T, U>(this T obj, Func<T, U> func)
{
    return obj == null ? default(U) : func(obj);
}

Your original code could then be rewritten as follows:

SomeSource.Where(srcItem => (srcItem.AndAnd(val => val.DataMembers["SomeText"]).AndAnd(val => val.Connection).AndAnd(val => val.ConnectedTo) as Type1).AndAnd(val => val.Handler).AndAnd(val => val.ForceInvocation));

Do be careful when returning non-boolean value types using this method - make sure you are familiar with the values returned by default(U).

0
Miguel Madero On

A while ago I wrote a project that mimics AndAnd that relies on DynamicProxy. It works fine, although I've not used it in prod. The only drawback is that it requires all of the members to be virtual or the returned types to be an interface so DynamicProxy can do its magic.

Check it here https://bitbucket.org/mamadero/andand/overview