Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I have been having a little bit of a debate with a coworker lately. We are specifically using C#, but this could apply to any language with nullable types. Say for example you have a value that represents a maximum. However, this maximum value is optional. I argue that a nullable number would be preferable. My coworker favors the use of zero, citing precedent. Granted, things like network sockets have often used zero to represent an unlimited timeout. If I were to write code dealing with sockets today, I would personally use a nullable value, since I feel it would better represent the fact that there is NO timeout.

Which representation is better? Both require a condition checking for the value meaning "none", but I believe that a nullable type conveys the intent a little bit better.

share|improve this question
5  
If a number is used, put it in a constant, not directly in the code. –  Renato Dinhani Conceição Mar 30 '12 at 17:31
    
@RenatoDinhaniConceição that can't be a general rule. Otherwise you end up softcoding everything. –  Simon Apr 3 '12 at 8:54
add comment

10 Answers

up vote 23 down vote accepted

Consider:

  • Language,

  • Framework,

  • Context.

1. Language

Using ∞ can be a solution for a maximum.

  • JavaScript, for example, has an infinity. C# doesn't¹.

  • Ada, for example, has ranges. C# doesn't.

In C#, there is int.MaxValue, but you cannot use it in your case. int.MaxValue is the maximum integer, 2,147,483,647. If in your code, you have a maximum value of something, like a maximum accepted pressure before something explodes, using 2,147,483,647 has no sense.

2. Framework

.NET Framework is rather inconsistent on this point, and its usage of magic values can be criticized.

For example, "Hello".IndexOf("Z") returns a magic value -1. It maybe makes it easier (does it?) to manipulate the result:

int position = "Hello".IndexOf("Z");
if (position > 0)
{
    DoSomething(position);
}

rather than using a custom structure:

SearchOccurrence occurrence = "Hello".IndexOf("Z");
if (occurrence.IsFound)
{
    DoSomething(occurrence.StartOffset);
}

but is not intuitive at all. Why -1 and not -123? A beginner may also mistakenly think that 0 means "Not found" too or just mistype (position >= 0).

3. Context

If your code is related to timeouts in network sockets, using something which was used by everyone for decades for the sake of being consistent is not a bad idea. Especially, 0 for a timeout is very clear: it's a value which cannot be zero. Using a custom class in this case may make things more difficult to understand:

class Timeout
{
    // A value indicating whether there is a timeout.
    public bool IsTimeoutEnabled { get; set; }

    // The duration of the timeout, in milliseconds.
    public int Duration { get; set; }
}
  • Can I set Duration to 0 if IsTimeoutEnabled is true?
  • If IsTimeoutEnabled is false, what happens if I set Duration to 100?

This can lead to multiple mistakes. Imagine the following piece of code:

this.currentOperation.Timeout = new Timeout
{
    // Set the timeout to 200 ms.; we don't want this operation to be longer than that.
    Duration = 200,
};

this.currentOperation.Run();

The operation runs for ten seconds. Can you see what's wrong with this code, without reading the documentation of Timeout class?

Conclusion

  • null expresses well the idea that the value is not here. It's not provided. Not available. It's neither a number, nor a zero/empty string or whatsoever. Don't use it for maximum or minimum values.

  • int.MaxValue is strongly related to the language itself. Don't use int.MaxValue for a maximum speed limit of Vehicle class or a maximum acceptable speed for an aircraft, etc.

  • Avoid magic values like -1 in your code. They are misleading and lead to mistakes in code.

  • Create your own class which would be more straightforward, with the minimum/maximum values specified. For example VehicleSpeed can have VehicleSpeed.MaxValue.

  • Don't follow any previous guideline and use magic values if it's a general convention for decades in a very specific field, used by most people writing code in this field.

  • Don't forget to mix approaches. For example:

    class DnsQuery
    {
        public const int NoTimeout = 0;
    
        public int Timeout { get; set; }
    }
    
    this.query.Timeout = 0; // For people who are familiar with timeouts set to zero.
    // or
    this.query.Timeout = DnsQuery.NoTimeout; // For other people.
    

¹ You can create your own type which includes infinity. Here, I'm talking about native int type only.

share|improve this answer
1  
"using something which was used by everyone for decades for the sake of being consistent is not a bad idea"/"Don't follow any previous guideline and use magic values if it's a general convention for decades in a very specific field, used by most people writing code in this field." - There's a typo somewhere, I think? –  deworde Mar 30 '12 at 17:27
1  
@deworde I believe MainMa is referring to the guidelines he himself gave above that one. –  Joshua Drake Mar 30 '12 at 19:55
1  
I disagree on the indexOf example, as -1 is outside the string, which Z most certainly is. –  Joshua Drake Mar 30 '12 at 19:57
5  
"JavaScript, for example, has an infinity. C# doesn't." - huh? –  BlueRaja - Danny Pflughoeft Mar 30 '12 at 20:36
    
+1 especially for "Create your own class" which is what I would have suggested. Any time a bare int isn't expressing enough about the type to constrain the problem, consider a new struct with more info (const instances of the struct that represent magic values, for example, or an enum on it to indicate). Or consider contract programming or some other solutions, but I think a custom struct is most straightforward. –  CodexArcanum Apr 2 '12 at 15:10
add comment

Null isn't any better than a magic number.

The important thing is to NAME the values that have magic effects, if you have to have such values, and to make sure that the definitions of those names are someplace that will be seen by anybody who bumps into the magic value and wtf's.

if (timeout == 4298435) ... // bad.
if (timeout == null) ... // bad.
if (timeout == NEVER_TIME_OUT) ... // yay! puppies and unicorns!
share|improve this answer
2  
Ok, maybe it does depend more on the language, but in C#, you would likely do: if (timeout.HasValue) instead of a direct comparison to null. –  Matt H Mar 30 '12 at 16:59
2  
Null isn't any worse than magic number. With magic numbers, you never know what the magic number is... it could be 0, -1, or something else. null is just null. –  marco-fiset Mar 30 '12 at 17:22
7  
Null means absence of value. This is the concept that a lot of magic numbers are intending to express. Being able to use null with a nullable type is a MUCH better solution than picking one arbitrary value from the range of possible values for a data type. –  17 of 26 Mar 30 '12 at 17:32
2  
Using "null" as your magic value, if your type has "null," is fine. The important thing is to NAME it, because sure as shootin' the next guy to come along will not know what you meant. Null can mean "infinity," "not yet specified," "bug in the code that created the data structure," or any number of other things. Only a name lets the next coder know that you meant that value to be there, and what behavior you meant it to trigger. –  mjfgates Mar 30 '12 at 17:49
1  
@CodeInChaos: I know you can do both, but I prefer HasValue. I actually am not a big fan of null in general, but nullable types using HasValue feels a little closer to an Option/Maybe type to me, which I am a fan of. –  Matt H Mar 30 '12 at 20:53
show 3 more comments

MAGIC_NUMBER code should absolutely always be avoided wherever possible. null is a much clearer expression of intent.

share|improve this answer
add comment

In C#, many CLR classes have a static Empty member:

  • System.String.Empty
  • System.EventArgs.Empty
  • System.Guid.Empty
  • System.Drawing.Rectangle.Empty
  • System.Windows.Size.Empty

This keeps you from having to remember whether to use a magic value or use null in order to construct an empty object.

But what if you're dealing with a simple value type like an int? In that case, consider whether you're falling victim to Primitive Obsession. It's quite possible that your apparently simple numeric property would benefit from its own class or struct, which would allow you to specify the Empty member and also add other behavior specific to that kind of value.

share|improve this answer
add comment

In this case, the null value is a great way to indicate there is no maximum. Generally when the special case means that the value in question does not apply, that you just don't want the feature it configures, null is a good indication of this.

A problem with using null to represent special cases is that there is only one null value, and there may be multiple special cases. In this case, I would pass an enumeration as an additional parameter, which can indicate a special case, or to use the int value normally. (This is essentially what the Nullable<> does for you, though it uses a boolean instead of an enum and combines the parameters into a single structure.)

share|improve this answer
add comment

In this case, I think a nullable type makes perfect sense.

Null means absence of value. This is a distinctly different concept than a number having a value of 0.

If you want to say "If I don't give you a value, use the maximum" then passing in null is the exact correct way to express that.

share|improve this answer
add comment

Null is worse to use than MagicNumber. Null represents the idea expressed better, but it isn't consistent across platforms in how it behaves, using MagicNumber always works the same which is beneficial.

depending on the environment/language used null could

  • simply be 0
  • may not be a legal value
  • may produce unexpected results due to three way logic

MagicNumber always behaves the same.

share|improve this answer
add comment

If you forget to check for the magic number (going to happen right), then a magic number will continue for a little while with nonsensical data. Much better to have a null that causes an exception as soon as possible.

share|improve this answer
add comment

Null: common error value, unspecified, void, or absence of value.

Zero: An actual, but not necessarily logical or intuitive value (in this context). Also a common value in initialization.

In the context of your problem, the timeoutInMilliseconds property is optional and there is no mention that the overhead of this approach would disqualify it as an option.

Conclusion: There are exceptions, and solutions vary by languages and domain; in this case, I would choose Null. Where (I believe) some people get this wrong is when they do not separate the data from the interface well. They just expect any client to read the documentation (or implementation) to determine how these special values are to be used/handled -- the special cases leak into the client's program and it can be quite unclear. By adding a good abstraction layer, the usage can be much clearer.

share|improve this answer
add comment

Null isn't the only alternative to a magic number.

public static int NO_TIMEOUT = 0;  // javaish

Null is evil. In the example above you might be able to get away with it since the code would obviously be able to handle a null. But in general what happens when you start passing nulls around is that sooner or later you get a null pointer exception. It may not happen when you first write the code, but code is maintained for far longer than just the first release. It's often maintained by people who don't know as much about the system as the original developers.

Scala (for example) has a nice alternative in the Option class. The Option class has one of two values, Some - which wraps the value you really want and None - which has no value.

That makes it obvious to any developer that there may not be a value and you had better code for that. Well, it should make it obvious anyway.

And not all magic numbers are a problem. Depending on context 0, 1, 1024 etc. can all be obvious. 347? Yeah, that one you should avoid. :-)

share|improve this answer
4  
-1: Justify "null is evil". –  deworde Mar 30 '12 at 17:19
4  
Defining an alias for a number doesn't change the fact that it's still a magic number. –  17 of 26 Mar 30 '12 at 17:33
    
Well, perhaps you have a different definition of magic number than I do. Please see en.wikipedia.org/wiki/… –  Jon Strayer Apr 2 '12 at 14:13
1  
I agree with Jon Strayer here. Null is an example of a ADT in a language that doesn't actually support ADT's. The OP can probably get away with it here but in general I consider any language that has null to have failed it's programmers slightly. –  Jeremy Wall Apr 3 '12 at 3:35
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.