lox
lox

Reputation: 1622

Checking for null in an object hierarchy

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.

Upvotes: 7

Views: 6555

Answers (12)

Rusty Speidel
Rusty Speidel

Reputation: 41

Here's my simple solution:

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

Here any of these objects (applicationForm, employeeInfo, workingConditions, salary) can be null and it will resolve without error.

Upvotes: 4

James Quox
James Quox

Reputation: 21

I liked the answer by Pontus Bremdahl, but added some more detail for my uses. Code:

    /// <summary>
    /// Get a member in an object hierarchy that might contain null references.
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <typeparam name="TResult"></typeparam>
    /// <param name="source">Base object to get member from.</param>
    /// <param name="getResult">Member path.</param>
    /// <param name="defaultResult">Returned object if object hierarchy is null.</param>
    /// <returns>Default of requested member type.</returns>
    public TResult SafeGet<TSource, TResult>(TSource source, Func<TSource, TResult> getResult, TResult defaultResult)
    {
        // Use EqualityComparer because TSource could by a primitive type.
        if (EqualityComparer<TSource>.Default.Equals(source, default(TSource)))
            return defaultResult;
        try
        {
            return getResult(source);
        }
        catch
        {
            return defaultResult;
        }
    }
    /// <summary>
    /// Get a member in an object hierarchy that might contain null references.
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <typeparam name="TResult"></typeparam>
    /// <param name="source">Base object to get member from.</param>
    /// <param name="getResult">Member path.</param>
    /// <returns>Default of requested member type.</returns>
    public TResult SafeGet<TSource, TResult>(TSource source, Func<TSource, TResult> getResult)
    {
        // Use EqualityComparer because TSource could by a primitive type.
        if (EqualityComparer<TSource>.Default.Equals(source, default(TSource)))
            return default(TResult);
        try
        {
            return getResult(source);
        }
        catch
        {
            return default(TResult);
        }
    }

Usage:

// Only authenticated users can run this code
if (!HttpContext.Current.SafeGet(s => s.User.Identity.IsAuthenticated))
        return;

// Get count limit from app.config
var countLimit = int.Parse(ConfigurationManager.AppSettings.SafeGet(
    s => s.Get("countLimit"),
      "100" // Default 100 if no value is present
    ));

// Is int 6 a class? Always no, but just to show primitive type usage.
var is6AClass = 6.SafeGet(i => i.GetType().IsClass);

Update

CSharp version 6 now has this built into it. https://github.com/dotnet/roslyn/wiki/New-Language-Features-in-C%23-6#null-conditional-operators

Null-conditional operators

Sometimes code tends to drown a bit in null-checking. The null-conditional operator lets you access members and elements only when the receiver is not-null, providing a null result otherwise:

int? length = customers?.Length; // null if customers is null
Customer first = customers?[0];  // null if customers is null

The null-conditional operator is conveniently used together with the null coalescing operator ??:

int length = customers?.Length ?? 0; // 0 if customers is null

The null-conditional operator exhibits short-circuiting behavior, where an immediately following chain of member accesses, element accesses and invocations will only be executed if the original receiver was not null:

int? first = customers?[0].Orders.Count();

This example is essentially equivalent to:

int? first = (customers != null) ? customers[0].Orders.Count() : null;

Except that customers is only evaluated once. None of the member accesses, element accesses and invocations immediately following the ? are executed unless customers has a non-null value.

Of course null-conditional operators can themselves be chained, in case there is a need to check for null more than once in a chain:

int? first = customers?[0].Orders?.Count();

Note that an invocation (a parenthesized argument list) cannot immediately follow the ? operator – that would lead to too many syntactic ambiguities. Thus, the straightforward way of calling a delegate only if it’s there does not work. However, you can do it via the Invoke method on the delegate:

if (predicate?.Invoke(e) ?? false) { … }

We expect that a very common use of this pattern will be for triggering events:

PropertyChanged?.Invoke(this, args);

This is an easy and thread-safe way to check for null before you trigger an event. The reason it’s thread-safe is that the feature evaluates the left-hand side only once, and keeps it in a temporary variable.

Upvotes: 1

Dax Fohl
Dax Fohl

Reputation: 10781

Use the Null monad. It can be in the same file or a different file so long as you using it.

public static class NullMonad {
    public static TResult SelectMany<TIn, TOut, TResult>(this TIn @in, Func<TIn, TOut> remainder, Func<TIn, TOut, TResult> resultSelector)
        where TIn : class
        where TOut : class
        where TResult : class {
        var @out = @in != null ? remainder(@in) : null;
        return @out != null ? resultSelector(@in, @out) : null;
    }
}

Then you can use LINQ:

var salary = from form in applicationForm
             from info in form.employeeInfo
             from cond in info.workingConditions
             select cond.salary

This will return the salary if it exists, or null if any of the prior statements result to null, without throwing an exception.

Is it that much better? No, just saves you the slightest bit of repetition, but it looks cool. It also avoids the overhead of creating all the unused "OrDefault" objects in the accepted answer.

Upvotes: 0

Pontus Bremdahl
Pontus Bremdahl

Reputation: 96

My solution would be something like:

public static TResult SafeGet<TSource, TResult>(this TSource source, Func<TSource, TResult> getResult) {
    if (source == null)
        return default(TResult);
    try {
        return getResult(source);
    }
    catch {
        return default(TResult);
    }
}

Usage:

Test myTestObject = null;
var myStringOrNull = myTestObject.SafeGet(x => x.test.test.test.mySring);

Upvotes: 0

James
James

Reputation: 82136

You need a recursive function to iterate through the structure and check each node and its children for null. I was working on a sample but IE crashed (typical!!). Will post one later.

Sample

You could do something as simple (assuming you only ever want to just check if the structure is valid) as this:

public void ValidateStructure()
{
    Node root = // get the root node
    try
    {
        ValidateChildren(root);
        Console.WriteLine("All nodes are valid");
    }
    catch (NullReferenceException)
    {
        Console.WriteLine("Structure contained a null node.");
    }

}

public void ValidateChildren(Node parent)
{
    // an NullReferenceException would be raised here since parent would be null 
    foreach (var child in parent.Children)
    {
        ValidateChildren(child);
    }
}

Upvotes: 0

Abel
Abel

Reputation: 57189

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 :)

Upvotes: 7

Gabe Moothart
Gabe Moothart

Reputation: 32112

You can nest ternary operators. Still a pain, but not as bad as nested ifs.

string salary = (applicationForm.employeeInfo == null) ? null :
                (applicationForm.employeeInfo.workingConditions == null) ? null :
                applicationForm.employeeInfo.workingConditions.salary;

If you just want to know whether or not it is null:

bool hasSalary = (applicationForm.employeeInfo == null) ? false :
                 (applicationForm.employeeInfo.workingConditions == null) ? false :
                 (applicationForm.employeeInfo.workingConditions.salary != null);

Upvotes: 4

Tom Neyland
Tom Neyland

Reputation: 6968

Since you didn't provide all too many details I had to fill in alot of the blanks. Here is a psuo-codeish example of how you might accomplish this via recursion

    public bool doesHierarchyContainANull(MyObject ParentObject)
    {

        if (ParentObject.getMemberToCheckForNull() == null)
            return true;
        else if (ParentObject.isLastInHierarchy())
            return false;

        return doesHierarchyContainANull(ParentObject.getNextInHierarchy());

    }

Upvotes: 1

IsmailS
IsmailS

Reputation: 10863

CheckForNull(MyType element)
{
    if(element.ChildElement != null)
    {
        CheckForNull(element.ChildElement);
    }
    else
    {
        Console.WriteLine("Null Found");
    }
}

Upvotes: 0

Wim
Wim

Reputation: 12092

Use reflection.

Create a helper method that takes a parent object and a hierarchical dot notated string for your property names. Then use PropertyInfo and use recursion to go down one property at a time each time checking for null and returning null if so, else continuing down the hierarchy.

Upvotes: 1

Fernando
Fernando

Reputation: 4028

can't you iterate?

for (SomeObject obj = someInstance; obj != null; obj = obj.Parent) {
    //do something
}

Upvotes: 2

Winston Smith
Winston Smith

Reputation: 21922

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?

Upvotes: 5

Related Questions