8

The thing that really bothers me the most about some programming languages (e.g. C#, Javascript) is that trying to access a property of null causes an error or exception to occur.

For example, in the following code snippet,

foo = bar.baz;

if bar is null, C# will throw a nasty NullReferenceException and my Javascript interpreter will complain with Unable to get value of the property 'baz': object is null or undefined.

I can understand this, in theory, but in real code I often have somewhat deep objects, like

foo.bar.baz.qux

and if any among foo, bar, or baz is null, my codes are broken. :( Further, if I evaluate the following expressions in a console, there seem to be inconsistent results:

true.toString() //evaluates to "true"
false.toString() //evaluates to "false"
null.toString() //should evaluate to "null", but interpreter spits in your face instead

I absolutely despise writing code to handle this problem, because it is always verbose, smelly code. The following are not contrived examples, I grabbed these from one of my projects (the first is in Javascript, the second is in C#):

if (!(solvedPuzzles && 
      solvedPuzzles[difficulty] && 
      solvedPuzzles[difficulty][index])) {
      return undefined;
   }
return solvedPuzzles[difficulty][index].star

and

if (context != null &&
   context.Request != null &&
   context.Request.Cookies != null &&
   context.Request.Cookies["SessionID"] != null) 
{
   SessionID = context.Request.Cookies["SessionID"].Value;
}
else
{
   SessionID = null;
}

Things would be so much easier if the whole expression returned null if any one of the properties was null. The above code examples could have been so much simpler:

return solvedPuzzles[difficulty][index].star;
    //Will return null if solvedPuzzles, difficulty, index, or star is null.

SessionID = context.Request.Cookies["SessionID"].Value;
    //SessionID will be null if the context, Request, Cookies, 
    //Cookies["SessionID"], or Value is null.

Is there something I'm missing? Why don't these languages use this behavior instead? Is it hard to implement for some reason? Would it cause problems that I'm overlooking?

Peter Olson
  • 139,199
  • 49
  • 202
  • 242
  • Is null a valid value for all those values in the chain? It's easier to just not store nulls in there in the first place. Then you can avoid those checks altogether. – R. Martinho Fernandes Aug 08 '11 at 15:14
  • 1
    You're gonna love the [Null Object pattern](http://en.wikipedia.org/wiki/Null_Object_pattern)... – fredoverflow Aug 08 '11 at 15:15
  • 3
    It's not the language's fault you decided to have a null collection rather than an empty collection... – canon Aug 08 '11 at 15:16
  • 1
    It's generally not good design to be following long chains of object references anyway. If you encapsulated the behaviour of your objects better then the null reference checks would still exist but they'd be in fewer places and more relevant to the context. See http://en.wikipedia.org/wiki/Law_of_Demeter – James Gaunt Aug 08 '11 at 15:17

5 Answers5

8

Would it cause problems that I'm overlooking?

Yes - it would cause the problem where you expect there to be non-null values, but due to a bug, you've got a null value. In that situation you want an exception. Failing silently and keeping going with bad data is a really bad idea.

Some languages (such as Groovy) provide a null-safe dereference operator which can help. In C# it might look something like:

SessionID = context?.Request?.Cookies?["SessionID"]?.Value;

I believe the C# team have considered this in the past and found it problematic, but that doesn't mean they won't revisit it in the future of course.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
2

And then if the tail end of that was a call to a method that returned bool, then what? Main.Child.Grandchild.IsEdit() and bool IsEdit();

I think it would be better to have a "null" instance that returns default behavior (this is known as the null object pattern).

That way, for others who expect a null to indicate a problem, they get their exception, but if you know a default object is acceptable, you can implement this and not worry about it. Both cases are then solved.

You could derive all "null" objects from INull, put them into a hash against their class name, then refer to that if a member is null. Then you can control default "null" implementation (what would be acceptable if the object is "null")

Also, "null" objects can throw exceptions if they are accessed, that way you know that you accessed one, and choose when this would be ok. So you can implement your own "null object exception".

If they put any language support for this, it would either be for everyone, across the board for the translation unit, or object by object with a qualifier (the last being the preferable option), at that point it wouldn't be default, and you could have already implemented a default "null" instance.

Lee Louviere
  • 5,162
  • 30
  • 54
1

Might I suggest that if you have to check for that many nulls, you are structuring your code poorly. Obviously, as I do not have your code, I cannot say that conclusively. I would suggest that you split your code into smaller functions. This way, your code only have to check one or maybe two nulls at a time, and can have more control over what happens if at any point something is null.

Levi Morrison
  • 19,116
  • 7
  • 65
  • 85
0

In general foo.Baz means "on the instance if foo, execute the baz member". This could be a dynamic language, a virtual member, or anything - but we've failed at the first step in the premise: there is no instance.

What you are edged is a null-safe member-access operator. Pretty rare, and frankly I disagree with your claim that this makes for smelly code (although I could argue that you are querying down too many levels in a single expression to be healthy).

Likewise, I disagree that null.toString() should return "null" - IMO it is perfecty normal for this to fail. Treating access on a null as "normal" is inherently wrong IMO.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • For types which are supposed to represent entities or mutable data holders, operations on `null` don't really make sense. For immutable reference types, however, the only thing that would be "wrong" with allowing types to make `default(someImmutableRefType).SomeMember()` do something useful would be the fact that the `struct` constraint on `Nullable` would mean there would be no "standard" way to define a nullable equivalent of that type. Otherwise I see no reason an immutable reference type shouldn't be able to work like a value type, including having a meaningful default value. – supercat Jan 07 '14 at 23:26
0

Is there something I'm missing? Why don't these languages use this behavior instead? Is it hard to implement for some reason? Would it cause problems that I'm overlooking?

When a variable is null when not expected the language you mention choose to fail early. This is a central concept when creating correctly working programs.

The option would be to hide the null pointer problem and the program would run one more row, but would probably crash later or even worse cause the wrong output!

Also when trying to fix a bug, it's way easier to fix it if it throws a NullReferenceException right away and the debugger shows you the line to start looking at. The alternative would be vague symptoms - Much harder to debug!

It's very common to program with null when you should not. In your above examles what does null mean. The answer is that it doesnt mean anything. As a reader of the code I would assume the writer forgot something.

What is usually the best solution is to introduce a small, what I call, "null object" that can be returned instead of null. In your example perhaps a "NoDifficulty" or "UnknownDifficulty" class could be returned to yield the result you are after in the end - clean code.

vidstige
  • 12,492
  • 9
  • 66
  • 110
  • Honestly `NullReferenceException` isn't far from a vague symptom, because the `x = null` line could be hundreds of miles away from the `y.foo()` line that throws. – R. Martinho Fernandes Aug 08 '11 at 15:36