2

I am using a lot of database data in my project that is exported in different classes. For example, I have

transaction.Layout.Multimedia.Images.first();

The problem is that these properties are not necessarily available.

So, it is possible that transaction.Layout is null, it is possible that transaction.Layout.Multimedia is null, and so on.

I currently use this for every property:

if (transaction.Layout != null)
{
    if (transaction.Layout.Multimedia != null)
    {
        if (transaction.Layout.Multimedia.Images != null)
        {
            if (transaction.Layout.Multimedia.Images.count > 0)
            {
                var img = transaction.Layout.Multimedia.Images.first();
            }
        }
    }
}

I was wondering if there is a better way that I can check all parent classes to make sure that the property I need is available. These aren't the only objects I use, there are others as well with totally different names.

Thanks in advance

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
Jerodev
  • 32,252
  • 11
  • 87
  • 108
  • i think this is what you looking for http://stackoverflow.com/questions/2080647/deep-null-checking-is-there-a-better-way – fubo Aug 18 '14 at 11:21
  • http://stackoverflow.com/questions/9066765/is-it-acceptable-to-use-exceptions-instead-of-verbose-null-checks is related. The simple answer is there is no syntactic magic to help you do this. – Chris Aug 18 '14 at 11:21
  • There is no short way but there will be "?." operator. Check [this](http://blogs.msdn.com/b/jerrynixon/archive/2014/02/26/at-last-c-is-getting-sometimes-called-the-safe-navigation-operator.aspx) – Mehmet Ataş Aug 18 '14 at 11:21

3 Answers3

8

No, there is not yet. The new version of .NET (Roslyn) has the Null propagating operator.

Then you could do:

if (transaction?.Layout?.Multimedia?.Image?.count > 0)
{
    var img = transaction.Layout.Multimedia.Images.first();
}

For now, we are stuck with this. You could minimize the rows needed by concatenating the checks, like this:

if ( transaction.Layout != null
     && transaction.Layout.Multimedia != null
     && transaction.Layout.Multimedia.Images != null
     && transaction.Layout.Multimedia.Images.count > 0
   )
{
    var img = transaction.Layout.Multimedia.Images.first();
}

There is nothing more to do.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
2

You can try to use Maybe monad. More detailed description is given at Chained null checks and the Maybe monad

With and If extension methods would allow you to write:

var img = transaction.With(x => x.Layout)
                     .With(x => x.Multimedia)    
                     .With(x => x.Images)
                     .If(x => x.count > 0))
                     .With(x => x.first());

With method looks like:

public static TResult With<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator)
  where TResult : class where TInput : class
{
  if (o == null) return null;
  return evaluator(o);
}

If method:

public static TInput If<TInput>(this TInput o, Func<TInput, bool> evaluator) 
  where TInput : class
{
  if (o == null) return null;
  return evaluator(o) ? o : null;
}
Ilya Ivanov
  • 23,148
  • 4
  • 64
  • 90
1

This is a bit OT but in theory, you could use extension methods to achieve a simpler syntax:

public static class GetterExtensions {
  public static LayoutClass GetLayout(this TransactionClass transaction) {
    if (transaction == null)
      return null;
    else
      return transaction.Layout;
  }
  public static MultimediaClass GetMultimedia(this LayoutClass layout) {
    if (layout == null)
       return null;
    else
       return layout.Multimedia;
  }
  public static ImagesClass GetImages(this MultimediaClass multimedia) {
    if (multimedia == null)
       return null;
    else
       return multimedia.Images;
  }
  public static int? GetCount(this ImagesClass images) {
    if (images == null)
       return null;
    else
       return images.count;
  }
}

(Where LayoutClass, MultimediaClass etc. are the types of the respective properties).

Having this, you could write

if (transaction.GetLayout().GetMultimedia().GetImages().GetCount() > 0)
  // ...

This solution makes use of the fact, that extension methods can be called on null objects without throwing an exception. They simply get null as their this parameter in this case. Also not the int? return type (Nullable<int>) of the GetCount() method, which allows to return null for an integer property.

But if you have many objects (with many properties) this solution is probably impractical and requires a lot of additiona maintenance effort. Also, writing GetLayout() instead of simply Layout is still less elegant.

MartinStettner
  • 28,719
  • 15
  • 79
  • 106