22

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.

2
  • 6
    If a number is used, put it in a constant, not directly in the code. Mar 30, 2012 at 17:31
  • @RenatoDinhaniConceição that can't be a general rule. Otherwise you end up softcoding everything. Apr 3, 2012 at 8:54

10 Answers 10

26

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.

7
  • 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, 2012 at 17:27
  • 1
    @deworde I believe MainMa is referring to the guidelines he himself gave above that one. Mar 30, 2012 at 19:55
  • 1
    I disagree on the indexOf example, as -1 is outside the string, which Z most certainly is. Mar 30, 2012 at 19:57
  • 5
    "JavaScript, for example, has an infinity. C# doesn't." - huh? Mar 30, 2012 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. Apr 2, 2012 at 15:10
13

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!
8
  • 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, 2012 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. Mar 30, 2012 at 17:22
  • 9
    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, 2012 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, 2012 at 17:49
  • 2
    -1: Null is better than a magic number. A magic number can easily be misinterpreted as data. Apr 2, 2012 at 20:25
10

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

6

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.

3

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.)

3

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.

1

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.

0

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.

-1

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.

-2

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. :-)

4
  • 5
    -1: Justify "null is evil".
    – deworde
    Mar 30, 2012 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, 2012 at 17:33
  • Well, perhaps you have a different definition of magic number than I do. Please see en.wikipedia.org/wiki/… Apr 2, 2012 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. Apr 3, 2012 at 3:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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