7

I have the following ugly code:

if (msg == null || 
    msg.Content == null || 
    msg.Content.AccountMarketMessage == null || 
    msg.Content.AccountMarketMessage.Account == null ||
    msg.Content.AccountMarketMessage.Account.sObject == null) return;

Is there a way to chain check for null values in C#, so that I don't have to check each individual level?

leppie
  • 115,091
  • 17
  • 196
  • 297
AngryHacker
  • 59,598
  • 102
  • 325
  • 594
  • Would be really nice, but no, there is not – Gusman Apr 22 '14 at 22:20
  • 1
    possible duplicate of [Null coalescing operator override](http://stackoverflow.com/questions/21051551/null-coalescing-operator-override). See [My Answer](http://stackoverflow.com/a/21051617/643085) in that question. – Federico Berasategui Apr 22 '14 at 22:22
  • 1
    I believe C#6 will let you do `if(msg.?Content.?AccountMarketMessage.?Account.?sObject == null) return;` – Lee Apr 22 '14 at 22:24

7 Answers7

9

One of the proposals in C# 6 would be to add a new Null Propogation operator.

This will (hopefully) allow you to write:

var obj = msg?.Content?.AccountMarketMessage?.Account?.sObject;
if (obj == null) return;

Unfortunately, there is nothing in the language at this point that handles this.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • I have thought about this for a while.. and I'm just not sure I like the syntax. Perhaps a double dot notation instead? `msg..Content..AccountMarketMessage..Account..sObject`. Mmm.. even that looks strange. Definitely looking forward to this though. – Simon Whitehead Apr 22 '14 at 22:25
  • @SimonWhitehead The second link I pasted would be a good place for suggesting alternatives. – Reed Copsey Apr 22 '14 at 22:26
  • Question mark seems OK, as it bears quite well established "nullable" conotation. – BartoszKP Apr 22 '14 at 22:26
  • @BartoszKP That is no doubt the reason it was chosen. It just doesn't feel natural to type `?.` next to each other (yet.. maybe it'll grow on me..). – Simon Whitehead Apr 22 '14 at 22:27
4

There is not currently such a thing, but it may be coming to .NET very soon. There is a well-known User Voice thread on the subject. And as noted in this article, the Visual Studio team has recently announced that:

We are seriously considering this feature for C# and VB, and will be prototyping it in coming months.

Edit: and as noted in Reed Copsey's answer above, it is now a planned addition for C# 6. There are better details on the Codeplex pages he linked.

Community
  • 1
  • 1
Kimberly
  • 2,657
  • 2
  • 16
  • 24
2

There is no built-in support for this, but you can use an extension method for that:

public static bool IsNull<T>(this T source, string path)
{
     var props = path.Split('.');
     var type = source.GetType();

     var currentObject = type.GetProperty(props[0]).GetValue(source);

     if (currentObject == null) return true;
     foreach (var prop in props.Skip(1))
     {
          currentObject = currentObject.GetType()
                .GetProperty(prop)
                .GetValue(currentObject);

         if (currentObject == null) return true;
     }

     return false;
}

Then call it:

if ( !msg.IsNull("Content.AccountMarketMessage.Account.sObject") )  return;
Selman Genç
  • 100,147
  • 13
  • 119
  • 184
2

You need monads and Monadic null checking. Could have a look at Monads.Net package. It can help with simplifying null tests and getting values from deep navigation properties

Something like

var sObject = person.With(p=>p.Content).With(w=>w.AccountMarketMessage ).With(p=>p.Account).With(p=>p.Object);

If you wanted a default value then

var sObject = person.With(p=>p.Content).With(w=>w.AccountMarketMessage).With(p=>p.Account).Return(p=>p.Object, "default value");
GraemeMiller
  • 11,973
  • 8
  • 57
  • 111
2

You can lazily evaluate the values using lambda expressions. This is overkill for a simple null check, but can be useful for chaining more complex expressions in a "fluent" manner.

Example

// a type that has many descendents
var nested = new Nested();

// setup an evaluation chain
var isNull =
    NullCheck.Check( () => nested )
        .ThenCheck( () => nested.Child )
        .ThenCheck( () => nested.Child.Child )
        .ThenCheck( () => nested.Child.Child.Child )
        .ThenCheck( () => nested.Child.Child.Child.Child );

// handle the results
Console.WriteLine( isNull.IsNull ? "null" : "not null" );

Code

This is a full example (albeit draft-quality code) that can be pasted into a console app or LINQPad.

public class Nested
{
  public Nested Child
  {
      get;
      set;
  }
}

public class NullCheck
{
   public bool IsNull { get; private set; }

   // continues the chain
   public NullCheck ThenCheck( Func<object> test )
   {
       if( !IsNull )
       {
           // only evaluate if the last state was "not null"
           this.IsNull = test() == null;
       }

       return this;
   }

   // starts the chain (convenience method to avoid explicit instantiation)
   public static NullCheck Check( Func<object> test )
   {
       return new NullCheck { IsNull = test() == null };
   }
}

private void Main()
{
   // test 1
   var nested = new Nested();
   var isNull =
       NullCheck.Check( () => nested )
           .ThenCheck( () => nested.Child )
           .ThenCheck( () => nested.Child.Child )
           .ThenCheck( () => nested.Child.Child.Child )
           .ThenCheck( () => nested.Child.Child.Child.Child );

   Console.WriteLine( isNull.IsNull ? "null" : "not null" );

   // test 2
   nested = new Nested { Child = new Nested() };
   isNull = NullCheck.Check( () => nested ).ThenCheck( () => nested.Child );

   Console.WriteLine( isNull.IsNull ? "null" : "not null" );

   // test 3
   nested = new Nested { Child = new Nested() };
   isNull = NullCheck.Check( () => nested ).ThenCheck( () => nested.Child ).ThenCheck( () => nested.Child.Child );

   Console.WriteLine( isNull.IsNull ? "null" : "not null" );
}

Again: you probably shouldn't use this in lieu of simple null checks due to the complexity it introduces, but it's an interesting pattern.

Tim M.
  • 53,671
  • 14
  • 120
  • 163
0

.NET Fiddle

As stated there is a plan to have c# 6.0 implement the ? operator to facilitate this process somewhat. If you cannot wait, I would suggest using a lambda expression and a simple helper function to solve this.

public E NestedProperty<T,E>(T Parent, Func<T,E> Path, E IfNullOrEmpty = default(E))
{
    try
    {
        return Path(Parent);
    }
    catch
    {
        return IfNullOrEmpty;
    }
}

This could be used int value = NestedProperty<First,int>(blank,f => f.Second.Third.id); as shown in the demo below

program

public class Program
{
    public void Main()
    {
        First blank = new First();
        First populated = new First(true);

        //where a value exists
        int value = NestedProperty<First,int>(blank,f => f.Second.Third.id);
        Console.WriteLine(value);//0

        //where no value exists
        value = NestedProperty<First,int>(populated,f => f.Second.Third.id);
        Console.WriteLine(value);//1

        //where no value exists and a default was used
        value = NestedProperty<First,int>(blank,f => f.Second.Third.id,-1);
        Console.WriteLine(value);//-1
    }

    public E NestedProperty<T,E>(T Parent, Func<T,E> Path, E IfNullOrEmpty = default(E))
    {
        try
        {
            return Path(Parent);
        }
        catch
        {
            return IfNullOrEmpty;
        }
    }
}

simple demo structure

public class First
{
    public Second Second { get; set; }
    public int id { get; set; }
    public First(){}
    public First(bool init)
    {
        this.id = 1;
        this.Second = new Second();
    }
}

public class Second
{
    public Third Third { get; set; }
    public int id { get; set; }
    public Second()
    {
        this.id = 1;
        this.Third = new Third();
    }
}

public class Third
{
    public int id { get; set; }
    public Third()
    {
        this.id = 1;
    }
}
Travis J
  • 81,153
  • 41
  • 202
  • 273
0

Since 3.5 (maybe earlier), You could write very simple extension method

  public static TResult DefaultOrValue<T, TResult> (this T source, 
                                                Func<T, TResult> property) where T : class
    {
        return source == null ? default(TResult) : property(source);
    }

You may name this method even shortier and then use like this

 var instance = new First {SecondInstance = new Second 
                          {ThirdInstance = new Third {Value = 5}}};
        var val =
            instance .DefaultOrValue(x => x.SecondInstance)
                .DefaultOrValue(x => x.ThirdInstance)
                .DefaultOrValue(x => x.Value);
        Console.WriteLine(val);
        Console.ReadLine();

so source classes are:

public class Third
{
    public int Value;
}

public class First
{
    public Second SecondInstance;
}

public class Second
{
    public Third ThirdInstance;
}
Roma Borodov
  • 596
  • 4
  • 10