124
Convert.ToString(null)

returns

null

As I expected.

But

Convert.ToString(null as object)

returns

""

Why are these different?

John MacIntyre
  • 12,910
  • 13
  • 67
  • 106

2 Answers2

150

There are 2 overloads of ToString that come into play here

Convert.ToString(object o);
Convert.ToString(string s);

The C# compiler essentially tries to pick the most specific overload which will work with the input. A null value is convertible to any reference type. In this case string is more specific than object and hence it will be picked as the winner.

In the null as object you've solidified the type of the expression as object. This means it's no longer compatible with the string overload and the compiler picks the object overload as it's the only compatible one remaining.

The really hairy details of how this tie breaking works is covered in section 7.4.3 of the C# language spec.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 15
    Ok. So it's using one overload instead of the other. Makes sense. But shouldn't both overloads return the same thing? +1 btw. – John MacIntyre Apr 27 '12 at 18:22
  • 2
    @JohnMacIntyre - That depends on the development team not the compiler. – JonH Apr 27 '12 at 18:22
  • 8
    @JohnMacIntyre If you look at the implementation of `Convert.ToString(string)` it's just an identity function while `Convert.ToString(object)` actually goes through a harder to follow path. At a glance I would agree they should return the same but the convertible layer of the BCL is not something I'm very knowledgable at and it's possible there is a good reason for the difference (i'm skeptical though) – JaredPar Apr 27 '12 at 18:24
  • I came here looking for how to convert a null object to a null string. The answer for other searchers is `(string)null`, or if you object is called o, then `(string)o` – rayzinnz Feb 23 '18 at 03:37
66

Following on from JaredPar's excellent overload resolution answer - the question remains "why does Convert.ToString(string) return null, but Convert.ToString(object) return string.Empty"?

And the answer to that is...because the docs say so:

Convert.ToString(string) returns "the specified string instance; no actual conversion is performed."

Convert.ToString(object) returns "the string representation of value, or String.Empty if value is null."

EDIT: As to whether this is a "bug in the spec", "very bad API design", "why was it specified like this", etc. - I'll take a shot at some rationale for why I don't see it as big deal.

  1. System.Convert has methods for converting every base type to itself. This is strange - since no conversion is needed or possible, so the methods end up just returning the parameter. Convert.ToString(string) behaves the same. I presume these are here for code generation scenarios.
  2. Convert.ToString(object) has 3 choices when passed null. Throw, return null, or return string.Empty. Throwing would be bad - doubly so with the assumption these are used for generated code. Returning null requires your caller do a null check - again, not a great choice in generated code. Returning string.Empty seems a reasonable choice. The rest of System.Convert deals with value types - which have a default value.
  3. It's debatable whether returning null is more "correct", but string.Empty is definitely more usable. Changing Convert.ToString(string) means breaking the "no actual conversion" rule. Since System.Convert is a static utility class, each method can be logically treated as its own. There's very few real world scenarios where this behavior should be "surprising", so let usability win over (possible) correctness.
Community
  • 1
  • 1
Mark Brackett
  • 84,552
  • 17
  • 108
  • 152
  • Is it fair to say that's a bug in the spec? – John MacIntyre Apr 27 '12 at 18:42
  • 3
    that doesn't answer why it is like this. Saying it behaves like this because it's documented to behave like this is tautological. – CodesInChaos Apr 27 '12 at 18:42
  • 2
    @JohnMacIntyre IMO it is fair to say that it is very bad API design. – CodesInChaos Apr 27 '12 at 18:43
  • 7
    @CodeInChaos - not a tautology, unless you assume the docs are written based on observable behavior after the BCL was developed. That'd be a strange assumption, I think. IOW, it's not "documented to behave like this", it's "documented that it *will* behave like this" - ie., it's "*specified* to behave like this". – Mark Brackett Apr 27 '12 at 19:02
  • 8
    It only shifts the question to "why was it specified to behave like this" – CodesInChaos Apr 27 '12 at 19:05
  • Nulling an object still leaves you with an empty object: "" – Shawn Spencer May 05 '12 at 15:24
  • 2
    It is worth noting that it is actually technically possible for `Convert.ToString` to return `null` from the `object` overload. It is specified that it does not return `null` when the input *is* `null`, but when the input is *not* `null`, it calls the input's own `.ToString()` method. That method can be overridden to return `null`. This is probably not a good idea, but it doesn't break any of the runtime's rules to do so. With `class ToStringNull { public override string ToString() { return null; } }`, you'd get a `null` back from `Convert.ToString(new ToStringNull())`. – Jonathan Gilbert Sep 07 '16 at 20:35