views:

254

answers:

7

For example, if I want to call the following: person.Head.Nose.Sniff() then if I want to be safe I have to do the following:

if(person != null)
    if(person.Head != null)
        if(person.Head.Nose != null)
            person.Head.Nose.Sniff();

is there any easier way of formulating this expression?

+4  A: 

Not really, besides

 if (person != null && person.Head != null && person.Head.Nose != null) 
Dan Dumitru
+12  A: 

First you can take advantage of short-circuiting in the boolean logic operators and do something like:

if (person != null && person.Head != null && person.Head.Nose != null)
{
    person.Head.Nose.Sniff();
}

Also note that what you are doing goes against a design guideline for developing software that is known as Law of Demeter.

João Angelo
Agreed. In this case I may think about creating a method *person.Sniff()* which call *Head.Sniff()* (if not null) which call *Nose.Sniff()* (if not null). In that way, you just have to check if *person* is not null and you minimize the knowledge to friends as designed in Law of Demeter.
JoeBilly
*"I'd prefer it to be called the Occasionally Useful Suggestion of Demeter."* Martin Fowler http://haacked.com/archive/2009/07/14/law-of-demeter-dot-counting.aspx
Hightechrider
+2  A: 

You can use Fluent Parameter Validation

Giorgi
That looks interesting. In vb.net, properties cannot be referenced without being read or written, so one could make change most of the validation methods to properties to ensure that the Check() was performed. Not sure if there's any way to do that in C. I don't like using methods or properties of null objects, but a static dummy instance should work just as well as "null". Incidentally, the system does allow one to define a generic class which inherits Exception; such classes can be thrown and caught. Further inheritance is a pain, but not impossible.
supercat
A: 

I would get rid of any use of null and do something like this:

((Nose)person.BodyParts[BodyPart.Nose]).Sniff();

This would require some kind of base class or interface.

public abstract class BodyPart
{
    public bool IsDecapitated { get; private set; }

    public BodyPart(bool isDecapitated)
    {
        IsDecapitated = isDecapitated;
    } 
}
ChaosPandion
+1  A: 

The best way is just to use the && operator instead of nested if statements:

if (person != null && person.Head != null && person.Head.Nose != null)
{
    person.Head.Nose.Sniff();
}

Note that you technically could perform a similar null check using an expression tree. Your method would have a signature like this:

static bool IsNotNull<T>(Expression<Func<T>> expression);

...which would allow you to write code looking something like this:

if (IsNotNull(() => person.Head.Nose))
{
    person.Head.Nose.Sniff();
}

But this would involve reflection and would generally be much more difficult to follow in any sort of in-depth way compared to the && method.

Dan Tao
+3  A: 

You could use null objects instead of null values. Sniff would then do nothing if any objects in the call chain are null objects.

This would not throw an exception:

person.Head.Nose.Sniff(); 

Your null classes could look like this (you could also use them as singletons and have interfaces for IPerson, IHead and INose):

class NullPerson : Person {
  public override Head Head { get { return new NullHead(); }
}
class NullHead : Head {
  public override Nose Nose { get { return new NullNose(); }
}
class NullNose : Nose {
  public override void Sniff() { /* no-op */ }
}

As a side note, in Oxygene there's an operator for this:

person:Head:Nose:Sniff; 
Jordão
I thought about posting this answer... I just couldn't think of a good reason to have a `null` body part.
ChaosPandion
@ChaosPandion: LOL! This example is really bad for this discussion! Maybe a `Default` body part then, and never allow it to be null.
Jordão
+6  A: 

Here's another implementation along the lines of the also-mentioned Fluent Parameter Validation: Chained null checks and the Maybe monad

jemerick
+1 came here to mention that. I just read it over the weekend :)
Ahmad Mageed
+1 same here, have read it this very morning.
Audrius