Home » c# » Best syntax for a = (x == null) ? null : x.func()

Best syntax for a = (x == null) ? null : x.func()

Posted by: admin November 29, 2017 Leave a comment

Questions:

Basic question here – I have many lines of code that look something like:

var a = (long_expression == null) ? null : long_expression.Method();

Similar lines repeat a lot in this function. long_expression is different every time. I am trying to find a way to avoid repeating long_expression, but keeping this compact. Something like the opposite of operator ??. For the moment I’m considering just giving in and putting it on multiple lines like:

var temp = long_expression;
var a = (temp == null) ? null : temp.Method();

But I was curious if there is some clever syntax I don’t know about that would make this more concise.

Answers:

Well, you could use an extension method like this:

public static TResult NullOr<TSource, TResult>(this TSource source,
    Func<TSource, TResult> func) where TSource : class where TResult : class
{
    return source == null ? null : func(source);
}

Then:

var a = some_long_expression.NullOr(x => x.Method());

Or (depending on your version of C#)

var a = some_long_expression.NullOr(Foo.Method);

where Foo is the type of some_long_expression.

I don’t think I would do this though. I’d just use the two line version. It’s simpler and less clever – and while “clever” is fun for Stack Overflow, it’s not usually a good idea for real code.

Questions:
Answers:

I found this answer insightful.

By doing this assignment, you are propagating the null deeper into the system. You’ll have to write your null handling condition again (and again and again) to handle these propagations.

Instead of allowing execution to continue with nulls, replace the null with a better representation (quoted from link)

  1. If the null represents an empty collection, use an empty collection.
  2. If the null represents an exceptional case, throw an Exception.
  3. If the null represents an accidentally uninitialized value, explicitly initialize it.
  4. If the null represents a legitimate value, test for it – or even better use a NullObject that performs a null op.

In particular, replacing a null collection reference with an empty collection has saved me many null tests.

Questions:
Answers:
var x = "";

/* try null instead */
string long_expression = "foo";

var a = ((x = long_expression) == null) ? null : x.ToUpper();

/* writes "FOO" (or nothing) followed by newline */
Console.WriteLine(a);

The type of the initialization value of x must be compatible to the type of long_expression (here: string). Works with the Mono C# compiler, version 2.10.8.1 (from Debian package mono-gmcs=2.10.8.1-4).

Questions:
Answers:

I think the C# language could really use a new operator for this sort of logic – it’s quite common, and an operator would simplify countless lines of code out there.

We need something along the lines of the “??” or “if null then” operator, but that works as special ‘.’ dot operator with a “if not null” condition. For “??”, the first ‘?’ is like the ‘if’ part of the “?:” if-then, and the second ‘?’ represents ‘null’ like for nullable types. I think we need a “.?” operator which works just like ‘.’, except it simply evaluates to null if the left expression is null instead of throwing an exception. I guess it would be a “dot not null” operator (OK, so maybe “.!?” would be more logical, but let’s not go there).

So, your oh-so-common example could lose the redundant symbol name like this:

var a = long_expression.?Method();

There is a ton of C# code out there with nested null checks that would benefit greatly. Just think how nice it would if this:

if (localObject != null)
{
    if (localObject.ChildObject != null)
    {
        if (localObject.ChildObject.ChildObject != null)
            localObject.ChildObject.ChildObject.DoSomething();
    }
}

could become just:

localObject.?ChildObject.?ChildObject.?DoSomething();

There is also a ton of code where null checks that should be there are missing, resulting in occasional runtime errors. So, actually using the new ‘.?’ operator by default would eliminate such problems… It’s perhaps unfortunate that we couldn’t reverse the behavior of ‘.’ and ‘.?’ at this point, so only the new ‘.?’ throws an exception if the left side is null – it would be the cleaner and more logical way to go, but breaking changes are very bad. Although, one could argue that this particular change would be likely to fix a lot of hidden problems, and unlikely to break anything (only code that expects a null ref exception to be thrown). Oh, well… one can always dream…

The only downside to checking for the null is really the slight performance hit, which is most assuredly why ‘.’ doesn’t check for null. But, I really think the ability to just use ‘.?’ when you care about performance (and know the left side will never be null) would have been the better way to go.

On a similar note, having ‘foreach’ check for and ignore a null collection would have also been so much nicer. No more need to wrap most ‘foreach’ statements with “if (collection != null)”, or worse, develop the common habit of always using empty collections instead of null … which is fine in some cases, but an even worse performance issue than the null check when this is done in complex object trees where the majority of the collections are empty (which I’ve seen a lot). I think the good intention of not having ‘foreach’ check for null to provide better performance has backfired in the majority of cases.

Anders, it’s never too late to make such changes, and add a compiler switch to enable them!

Questions:
Answers:

You can write an extension method for whatever type long_expression evaluates to:

public static object DoMethod(this MyType pLongExpression)
{
   return pLongExpression == null ? null : pLongExpression.Method();
}

This will be callable on any MyType reference, even if that reference is null.

Questions:
Answers:

You could put long_expression == null into a function with a short name and just call that function each time.

Questions:
Answers:

if (!String.IsNullOrEmpty(x))
x.func()