Nullsafe dereference operator (?.) in C#


Nullsafe dereference operator (?.) as exists in some languages is a handy and nice pattern which saves a few bugs. Null references are serious problems and as people say nearly 70% of all bugs in the industry are directly or indirectly related to “Object reference not set to an instance of an object!”

However, there is noway to completely avoid null references, unless you write in a fully functional programming language like HASCUL. Some object oriented languages like groovy support a notion of nullsafe dereference operator which helps your null checks to be simpler. Here is the syntax in groovy:

bossName = Employee?.Supervisor?.Manager?.Boss?.Name

The above code returns the Name of the boss of the manager of the supervisor of an specific employee. It checks if any part of the right operand is null. If employee has no supervisor or supervisor has no manager, etc. bossName will become “null”.

Writing the above code in C# becomes like this:

bossName = Employee != null && Employee.Supervisor != null && Employee.Supervisor.Manager != null && Employee.Supervisor.Manager.Boss != null ? Employee.Supervisor.Manager.Boss.Name : null;

Pretty ugly, isn’t it? We can make a bunch of C# functions that takes us as closer to the groovy syntax of groovy. Note that nullsafe operator is going to be used in many places and inside the loops and it is not efficient to define any class for just a null check. Anyway a simple function won’t add much overhead.

//Groovy:
bossName = Employee?.Supervisor?.Manager?.Boss?.Name
 //C#:
 bossName = Nullify.Get(Employee, e => e.Supervisor, s => s.Manager, m => m.Boss, b => b.Name);

 

This one looks much more beautiful and more readable and this the the Nullify (static) class:

 public static class Nullify
 {
 public static TR Get<TF, TR>(TF t, Func<TF, TR> f) where TF : class
 {
 return t != null ? f(t) : default(TR);
 }

 public static TR Get<T1, T2, TR>(T1 p1, Func<T1, T2> p2, Func<T2, TR> p3)
 where T1 : class
 where T2 : class
 {
 return Get(Get(p1, p2), p3);
 }

 /// <summary>
 /// Simplifies null checking as for the pseudocode
 /// var r = Pharmacy?.GuildMembership?.State?.Name
 /// can be written as
 /// var r = Nullify( Pharmacy, p => p.GuildMembership, g => g.State, s => s.Name );
 /// </summary>
 public static TR Get<T2, T2, T3, TR>(T1 p1, Func<T1, T2> p2, Func<T2, T3> p3, Func<T3, TR> p4)
 where T1 : class
 where T2 : class
 where T3 : class
 {
 return Get(Get(Get(p1, p2), p3), p4);
 }
 }
 

UPDATE:

Below is the alternative way by using extension methods. It is really your preference to decide which way is better. Above approach might have slightly less overhead since it just adds a single function call in the IL code and is more concise for short expressions, but the code is shorter in the bottom approach and visually looks better for larger expressions.


 public static TOut NullSafe<TIn, TOut>(this TIn obj, Func<TIn, TOut> memberAction)
 {
 //Note we should not use obj != null because it can not test value types and also
 //compiler has to lift the type to a nullable type for doing the comparision with null.
 return (EqualityComparer<TIn>.Default.Equals(obj, default(TIn))) ? memberAction(obj) : default(TOut);

 }

 //Usage:..

 Employee.NullSafe( e => e.Supervisor ).NullSafe( s => s.Boss ).NullSafe( b => b.Name );

 
Advertisement
Posted in C#. 6 Comments »

6 Responses to “Nullsafe dereference operator (?.) in C#”

  1. Michiel Cornille Says:

    there should be a NOT (!) in front of the IEQualityComparer of you NullSafe extension

    public static TOut NullSafe(this TIn obj, Func memberAction)
    {
    //Note we should not use obj != null because it can not test value types and also
    //compiler has to lift the type to a nullable type for doing the comparision with null.
    return (!EqualityComparer.Default.Equals(obj, default(TIn))) ? memberAction(obj) : default(TOut);

    }

  2. Alex D Says:

    I do not understand your comment “Note we should not use obj != null because it can not test value types and also compiler has to lift the type to a nullable type for doing the comparision with null.” Why would you ever want to apply this to value types?

    The correct way to handle both reference and nullable types is the following:

    public static TOut NullSafe(
    this TIn obj,
    Func memberAction ) where TIn : class
    {
    return ReferenceEquals(obj, null) ? default(TOut) : memberAction(obj);
    }

    public static TOut NullSafe(
    this TIn? obj,
    Func memberAction ) where TIn : struct
    {
    return obj.HasValue ? memberAction(obj.Value) : default(TOut);
    }

  3. Alex D Says:

    Actually, it doesn’t make sense to return default(TOut) either. Eg, if Employee is null, his age should also be null, not 0. (The rest of the program logic might silently start to think you’re employing newborns in some hair-brained embezzling scheme.) The return type should never be a value type, it should be a nullable type.

    If the property is a value type, cast to a nullable inside the lambda like so:
    Employee.NullSafe( e => (double?) e.Sallary )

    To remind the users of this, rewrite the extension methods as follows:

    public static TOut NullSafe(this TIn obj, Func memberAction ) where TIn : class where TOut : class
    { return ReferenceEquals(obj, null) ? null : memberAction(obj); }

    public static TOut? NullSafe(this TIn obj, Func memberAction ) where TIn : class where TOut : struct
    { return ReferenceEquals(obj, null) ? null : memberAction(obj); }

    public static TOut NullSafe(this TIn? obj, Func memberAction ) where TIn : struct where TOut : class
    { return obj.HasValue ? memberAction(obj.Value) : null; }

    public static TOut? NullSafe(this TIn? obj, Func memberAction ) where TIn : struct where TOut : struct
    { return obj.HasValue ? memberAction(obj.Value) : null; }

    It might seem possible to get rid of the requirement to cast to Nullable through some generic magic. It almost is. Unfortunately, the compiler complains when two generic functions differ only in their generic type constraints and return value. But you can add the following extension methods to the mix:

    public static TOut? NullSafeV(this TIn obj, Func memberAction ) where TIn : class where TOut : struct
    { return ReferenceEquals(obj, null) ? (TOut?)null : memberAction(obj); }

    public static TOut? NullSafeV(this TIn? obj, Func memberAction ) where TIn : struct where TOut : struct
    { return obj.HasValue ? memberAction(obj.Value) : (TOut?)null; }

  4. Alex D Says:

    Oh, look, the comment system ate all the generic type specifiers…. hehe, how @#@# awesome.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: