How do I create a Null Object in C#
Asked Answered
L

5

15

Martin Fowler's Refactoring discusses creating Null Objects to avoid lots of

if (myObject == null)

tests. What is the right way to do this? My attempt violates the "virtual member call in constructor" rule. Here's my attempt at it:

public class Animal
{
    public virtual string Name { get; set; }
    public virtual string Species { get; set; }
    public virtual bool IsNull 
    { 
        get { return false; }
    }
}

public sealed class NullAnimal : Animal
{
    public override string Name
    {
        get{ return "NULL"; }
        set { }
    }
    public override string Species
    {
        get { return "NULL"; }
        set { }
    }
    public virtual bool IsNull
    {
        get { return true; }
    }
}
Lashondra answered 24/7, 2009 at 15:24 Comment(4)
What is the problem you're trying to solve? What is wrong with null references, exactly?River
refactoring.com/catalog/introduceNullObject.htmlLashondra
Your IsNull property in the NullAnimal class should be an override, not a virtual.Coxalgia
But, if we talk about functions, returns boolean types? What should i return ? False? True? It is not unequivocal, i think. Other thing, in public property "Species" you returns always null... So, if someone will do tests, he probably will get unexpected result. For example, someone gets "NullAnimal" object, but he consider it as "Animal". So, he can suppose, if he set Species to "Mammals", he will get "Mammals". But it is not. It is not so simple, IMHOCivility
B
11

I tend to agree with Wyatt Barnett's answer in that you should show restraint when creating these kinds of "null" objects. That said, there are some nice reasons for doing so. On occasion.

I also tend to agree with Supertux's answer in that the whole point of a null object is to not need to check whether or not it is null, so you should lose the IsNull property. If you really feel you need the IsNull property, then read Wyatt's response again and reconsider.

And thank you CraigTP for the nice links for more info. Good stuff.

Now I will assume that in your real code you actually have a constructor that is attempting to set the values of Name or Species (whatever your real code equivalent might be called). Otherwise, why would you get the "virtual member call in constructor" warning/error? I've run into a couple of similar problems when using the newfangled MyProperty { get; set; } shortcut myself (particularly when used in structs, and don't get me started about serialization versioning). Your solution is to not use the shortcut, but instead do it the old-fashioned way.

public class Animal {
    protected Animal() { }

    public Animal(string name, string species) {
        _Name = name;
        _Species = species;
    }

    public virtual string Name {
        get { return _Name; }
        set { _Name = value; }
    }
    private string _Name;

    public virtual string Species {
        get { return _Species; }
        set { _Species = value; }
    }
    private string _Species;
}

public sealed class NullAnimal : Animal {
    public override string Name {
        get { return String.Empty; }
        set { }
    }
    public override string Species {
        get { return String.Empty; }
        set { }
    }
}

This solves the problem of setting your virtual properties in the constructor. Instead, you are setting your private field values (something you don't have the ability to reference if you use the shortcut). For extra credit, compile both methods, and use the Reflector to look at the resulting assemblies.

The more I use the { get; set; } shortcut, the more I dislike it.

Bangle answered 24/7, 2009 at 21:53 Comment(0)
F
26

Go look up the amount of pain that interesting concepts, such as DbNull, have caused and think about if this is actually a good idea.

Protip: if you are constantly checking for null references, you probably should rethink the API a bit to help preclude null objects closer to the top of the stack.

Protip II: having something throw an exception when there is an unexpected null is actually fine and dandy. Things should go boom if you have nulls where there shouldn't be null.

Forepaw answered 24/7, 2009 at 15:29 Comment(7)
+1. Null pattern is an anti-pattern IMO. Throw for unwanted nulls and check for nulls when they're allowed.Gawky
The idea of the NullObject pattern is that things don't go boom - especially in producton.Comforter
@Wyatt: jeremyjarrell.com/archive/2007/08/01/46.aspx has a nice demonstration of the benefits of null objects...but I'm not a big fan of them, personally.Hearse
There is a place for null objects, such as sentinel nodes in a red-black tree, but I'm not convinced that they're generally a good idea, for the reasons mentioned by Randolpho.Allegedly
@supertux: which is better--app crashing and not sending bad data or sending bad data because you have some wierd anti-null fetish?Forepaw
@Wyatt It depends on the software really, in some cases, perhaps you wouldn't use a NullObject pattern. I'm not a fetishist - the question was about NullObjects. For example, in aviation or medical software - app-crashing is probably a bad thing. But in My Animals Project probably not so bad.Comforter
It's not some kind of "anti-null fetish". Calling it that not only disrespects the original poster, but there are some cases where an API could benefit by being forgiving. Comparing to DBNull / null isn't accurate, as DBNull does not inherit from null. Since NullAnimal is an Animal, passing NullAnimal to something expecting an Animal shouldn't cause problems. You can't say that about something that passes DBNull to something that expects null. All that said +1 for prompting thought on whether it's a good idea. In most cases, I tend to agree it is not.Bangle
B
11

I tend to agree with Wyatt Barnett's answer in that you should show restraint when creating these kinds of "null" objects. That said, there are some nice reasons for doing so. On occasion.

I also tend to agree with Supertux's answer in that the whole point of a null object is to not need to check whether or not it is null, so you should lose the IsNull property. If you really feel you need the IsNull property, then read Wyatt's response again and reconsider.

And thank you CraigTP for the nice links for more info. Good stuff.

Now I will assume that in your real code you actually have a constructor that is attempting to set the values of Name or Species (whatever your real code equivalent might be called). Otherwise, why would you get the "virtual member call in constructor" warning/error? I've run into a couple of similar problems when using the newfangled MyProperty { get; set; } shortcut myself (particularly when used in structs, and don't get me started about serialization versioning). Your solution is to not use the shortcut, but instead do it the old-fashioned way.

public class Animal {
    protected Animal() { }

    public Animal(string name, string species) {
        _Name = name;
        _Species = species;
    }

    public virtual string Name {
        get { return _Name; }
        set { _Name = value; }
    }
    private string _Name;

    public virtual string Species {
        get { return _Species; }
        set { _Species = value; }
    }
    private string _Species;
}

public sealed class NullAnimal : Animal {
    public override string Name {
        get { return String.Empty; }
        set { }
    }
    public override string Species {
        get { return String.Empty; }
        set { }
    }
}

This solves the problem of setting your virtual properties in the constructor. Instead, you are setting your private field values (something you don't have the ability to reference if you use the shortcut). For extra credit, compile both methods, and use the Reflector to look at the resulting assemblies.

The more I use the { get; set; } shortcut, the more I dislike it.

Bangle answered 24/7, 2009 at 21:53 Comment(0)
C
3

The point of the Null Object pattern is that it doesn't require a null check to prevent a crash or error.

For example if you tried to perform an operation on the Species property and it was null - it would cause an error.

So, you shouldn't need an isNull method, just return something in the getter that won't cause the app to crash/error e.g.:

public class Animal
{
    public virtual string Name { get; set; }
    public virtual string Species { get; set; }
}

public sealed class NullAnimal : Animal
{
    public override string Name
    {
        get{ return string.Empty; }
        set { ; }
    }
    public override string Species
    {
        get { return string.Empty; }
        set { ; }
    }
}
Comforter answered 24/7, 2009 at 15:33 Comment(2)
Fine, but this still violates the virtual member call in constructor rule.Lashondra
Technically not, because there is no constructor.Bangle
Y
2

You only use this approach if it is appropriate. Your example of an Animal object might not be a good example because it doesn't present an appropriate case where you would use this approach. For example:

Animal animal = new Animal();

if (animal.tail == null)
{
    //do nothing because wagging a tail that doesn't exist may crash the program
}
else
{
    animal.wagTail();
}

In this example, you should build the Animal object so that if the animal doesn't have a tail, it can successfully handle the wagTail() command without crashing.

Class Animal
{
    Tail tail;

    void wagTail()
    {
        if (this.tail == null)
        {
            //do nothing
        }
        else
        {
            this.tail.doTheWag();
        }
    }
}

Now you don't need to do a null check, but can just call animal.wagTail() regardless of whether the animal has a tail or not.

Yeah answered 24/7, 2009 at 15:57 Comment(0)
C
0

I'd like to mention here some interesting detail. Look at your class. Does it has any logic in it? This is not a class in its sense, this is a data structure. What you are trying to do is apply null object pattern to something it is not applicable to. Data structures is closer to value types, than to classes. There fore null check can be right in place to solve your problem. Null object pattern is not something you should always follow. Null object pattern is a thing you can use to avoid Liskov's substitution principle violation, to represent a class that does nothing, because null is not appropriate substitution for a class as it is a value, but not a class. But things are different with value types and data structures. Null is value! So in this case null check is the right thing to do.

Castora answered 7/5, 2014 at 9:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.