views:

263

answers:

6

I’m looking for a nice way to handle a null reference in object hierarchy.

ie:

if(null == Object1.Object2.Object3.Property)

This example will throw a Null Reference exception if say Object2 is null.

In my case I don't care what is null, just that something is. I don't really want to put try/catches around each place that I want to do something like this, so I’ve been looking for an alternative.

I've experimented with the ?? operator but this makes for some ugly looking code after two levels.

Any ideas appreciated.

+2  A: 

This (null-safe dereferencing) is something that gets raised occasionally, and no; there is no tidy answer at the moment, other than:

if(Object1 == null || Object1.Object2 == null
        || Object1.Object2.Object3 == null
        || Object1.Object2.Object3.Property == null)

You can do some minor caching if you need (by introducing variables) but that gets even uglier:

SomeType2 obj2;
SomeType3 obj3;
if(Object1 == null || (obj2 = Object1.Object2) == null
        || (obj3 = obj2.Object3) == null
        || obj3.Property == null)

In general, I would advise against the above unless you really, really don't want to call a property twice (because it does more work than a property should)

Marc Gravell
A: 

In general if you don't care which property was null in your object hierarchy just don't test against it. Use a global error handler for the application (this will depend on the type of application ASP.NET, WinForms, ...) and say the user something went wrong.

Darin Dimitrov
+1  A: 

Not directly answering your question - just some hints:

  • try using Null object
  • it just don't seem appropriate to go through the hierarchy this far (your three levels), maybe some method on the first/second level that would answer the condition would help
Rashack
+2  A: 

You could use the Null Object pattern.

class Car {
    Engine engine = null;

    public Engine Engine { 
        get { 
            return engine ?? new NullEngine();
        }
    }
}

class Engine {
    string make;
    public virtual string Make { get { return make; } }
}

class NullEngine : Engine {
    public override string Make { get { return null; } }
}

Then you could do:

Car car;
if (car.Engine.Make != null) Console.WriteLine(car.Engine.Make);

Instead of:

Car car;
if (car.Engine != null && car.Engine.Make != null) Console.WriteLine(car.Engine.Make);

Note that it is quite a lot of work to define "null objects" for your entire model. You also need to take great care so that you do not confuse the users of your model. Code tend to blow up quickly if you pass null around and forget to check for it, but a "null object" can easier survive deep into the callstack and cause subtle problems since it isn't a real object.

Hallgrim
You indeed need to watch out for the correct places to use this (think about what you do), but this pattern - also known as the Sentinel pattern, as far as I know? - can be a real life saver. I like it and use it whenever it adds its weight, so to speak.
peSHIr
+6  A: 

Now this might be on a tangent... but I'd suggest a design change to avoid the ugliness and pain

Calling Object1.Object2.Object3.Property violates the law of demeter. Instead if you're supposed to get to that property, Object1 should expose a Property itself... So you should be calling Object1.RetrievedFromTheDepthsProperty
Why this is needed.. is that if the designer of the Type Object2 changes the type of Object returned by the 'Object3' field/property to one that doesn't have the Property you're looking for, you'd be hosed. The client knows too much about the internal structure of Object1. If Object1 encapsulated where the data is located internally, you'd be safe against future changes. Also this property can do all the null checking internally as required... leaving you with the much cleaner

if (Object1.RetrievedFromTheDepthsProperty == null) {...}
Gishu
A: 

I'd strongly suggest that you look into the following post: http://www.hardcodet.net/2008/12/observe-dependencies-through-lambda-expressions-part1

It refers to a similar issue, and also allows you to easily handle changes.

SaguiItay