Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking for null in an object hierarchy

Tags:

c#

c#-3.0

I have a large C# (3.0) object structure originating from a deserialized XML document. I need to know whether a variable deep in the hierarchy is null. The way I do this now is to check every parent object on the way down for null, but this leads to a long repetition of if statements.

I am trying to avoid expensive try-catch blocks.

Is there a smarter way to do this?

edit: For example after a deserialization of an XML application form into an object hierarchy structure there could potentially be a salary value under

applicationForm.employeeInfo.workingConditions.salary

but to find out safely I have to write something like

if (applicationForm.employeeInfo != null)
  if (applicationForm.employeeInfo.workingConditions != null)
    if (applicationForm.employeeInfo.workingConditions.salary != null)

because simply using the latter if-statement will of course fail if one of the parent objects is null.

So I am looking for any smarter way to handle this situation.

like image 447
lox Avatar asked Nov 04 '09 13:11

lox


2 Answers

You've run into the classic situation where each step in A.B.C.D may yield null. Though this is a common scenario, surprisingly there's no common pattern for solving it, other then using a large if-statement with lots of or's (||).

If each step can return a different class, then there's a rarely used pattern that you can apply: Use a generalized generic extension method with method chaining.

A generalized extension method is not a fixed term but I use it here for emphasizing that the ext. method is applicable to almost all types of objects, hence generalized. According to Bill Wagner in Effective C#, this is bad design. But in some remote cases, like yours, you can use it as long as you know what you're doing and why.

The trick is simple: define a generic extension method and generalize it for all classes that have a default constructor. If the test fails (object is null), the method returns a new object of the same type. Otherwise, it will return the unchanged object itself.

Why is this a good approach for your scenario? Because you don't need to change any existing classes, because it's understandable and promotes readable code, because it keeps type safety (compile time errors instead of runtime errors) and it's simply more concise compared to other approaches.

// extension method:
public static class SomeExtentionMethods
{
    public static T SelfOrDefault<T>(this T elem)
        where T : class, new()     /* must be class, must have ctor */
    {
        return elem ?? new T();    /* return self or new instance of T if null */
    }
}

// your code now becomes very easily readable:
Obj someObj = getYourObjectFromDeserializing();

// this is it applied to your code:
var mySalary = applicationForm.SelfOrDefault().
    employeeInfo.SelfOrDefault().
    workingConditions.SelfOrDefault().
    salary;

// now test with one if-statement:
if(mySalary.IsEmpty())
   // something in the chain was empty
else
   // all's well that ends well :)

The beauty of this approach is that it works with all types of classes (provided they have a ctor), including collections and arrays. If any step is an index step, it will still work (depending on the collection, an invalid index can return null, default or raise an exception):

var x = 
    someObj.SelfOrDefault()
    .Collection.SelfOrDefault()
    .Items[1].SelfOrDefault()
    .Mother.SelfOrDefault()
    .Father.SelfOrDefault();

Update: expanded a bit and added a more elaborate example
Update: renamed NotNull, which implies boolean, to SelfOrDefault, which follows LINQ's naming convention (FirstOrDefault etc) and implies what it does.
Update: rewritten and reorganized, made code more applicable, hoping to make it more understandable overall :)

like image 138
Abel Avatar answered Sep 25 '22 23:09

Abel


Firstly, if you're repeating the logic in more than one place, encapsulate it in a method.

Secondly, you don't need lots of if statements, you just need one with a lot of OR conditions:

if(parent==null || 
   parent.Child == null || 
   parent.Child.GrandChild == null ...

Thirdly, "avoiding expensive try/catch blocks" may be a premature optimization, depending on your scenario. Have you actually tried this and profiled it, and is it really causing a large overhead?

like image 25
Winston Smith Avatar answered Sep 23 '22 23:09

Winston Smith