Compiler Value Type Resolution and Hardcoded “0” Integer Values

It’s because a zero-integer is implicitly convertible to an enum:

enum SqlDbType
{
    Zero = 0,
    One = 1
}

class TestClass
{
    public TestClass(string s, object o)
    { System.Console.WriteLine("{0} => TestClass(object)", s); } 

    public TestClass(string s, SqlDbType e)
    { System.Console.WriteLine("{0} => TestClass(Enum SqlDbType)", s); }
}

// This is perfectly valid:
SqlDbType valid = 0;
// Whilst this is not:
SqlDbType ohNoYouDont = 1;

var a1 = new TestClass("0", 0);
// 0 => TestClass(Enum SqlDbType)
var a2 = new TestClass("1", 1); 
// => 1 => TestClass(object)

(Adapted from Visual C# 2008 Breaking Changes – change 12)

When the compiler performs the overload resolution 0 is an Applicable function member for both the SqlDbType and the object constructors because:

an implicit conversion (Section 6.1) exists from the type of the argument to the type of the corresponding parameter

(Both SqlDbType x = 0 and object x = 0 are valid)

The SqlDbType parameter is better than the object parameter because of the better conversion rules:

  • If T1 and T2 are the same type, neither conversion is better.
    • object and SqlDbType are not the same type
  • If S is T1, C1 is the better conversion.
    • 0 is not an object
  • If S is T2, C2 is the better conversion.
    • 0 is not a SqlDbType
  • If an implicit conversion from T1 to T2 exists, and no implicit conversion from T2 to T1 exists, C1 is the better conversion.
    • No implicit conversion from object to SqlDbType exists
  • If an implicit conversion from T2 to T1 exists, and no implicit conversion from T1 to T2 exists, C2 is the better conversion.
    • An implicit conversion from SqlDbType to object exists, so the SqlDbType is the better conversion

Note that what exactly constitutes a constant 0 has (quite subtly) changed in Visual C# 2008 (Microsoft’s implementation of the C# spec) as @Eric explains in his answer.

Leave a Comment