Compiler type value Resolution and hardcoded values ​​"0" Integer values

First, a little background. Read the question and the accepted answer posted here for a specific scenario for my question. I am not sure if there are other similar cases, but this is the only case that I know of.

The aforementioned "quirk" is something that I have long been aware of. Until recently, I did not understand the fullness of the matter.

The Microsoft documentation in the SqlParameter class sheds a bit more light on the situation.

When you specify Object in a value parameter, SqlDbType is inferred from the Microsoft.NET Framework type of the object.

Use caution when using this SqlParameter constructor SqlParameter to specify integer parameter values. Since this overload occupies a value of type Object , you must convert the integral value to Object enter when the value is zero , as the following C # example demonstrates.

Parameter = new SqlParameter("@pname", Convert.ToInt32(0));

If you do this, do not perform this conversion, the compiler assumes that you are trying to invoke the SqlParameter (string, SqlDbType) constructor overload.

(added)

My question is, why does the compiler assume that when you specify a hard-coded β€œ0” (and only the value β€œ0”) that you are trying to specify an enumeration type, not an integer type? In this case, it is assumed that you declare a SqlDbType value, not a value of 0.

This is unintuitive, and, even worse, the error is inconsistent. I have old applications that I wrote that called stored procedures for years. I will make changes to the application (often sometimes not even related to my SQL Server classes), post an update, and this problem will suddenly break the application.

Why did the compiler get confused in the value 0 when an object containing several method signatures contains two identical signatures, where one parameter is an object / integer and the other accepts an enumeration?

As I mentioned, I never saw this to be a problem with any other constructor or method in any other class. Is this unique to the SqlParameter class or is it an inheritance error inside C # /. Net?

+18
compiler-construction c #
Jan 08 '13 at
source share
5 answers

This is because a null integer is implicitly converted to an enumeration:

 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 overload resolution, 0 is the Applicable function element for the SqlDbType and object constructors, because:

there is an implicit conversion (section 6.1) from the argument type to the type of the corresponding parameter

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

The SqlDbType parameter SqlDbType better than the object parameter due to better conversion rules :

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

Note that what exactly constitutes the constant 0 has (quite subtly) changed in Visual C # 2008 (Microsoft's implementation of the C # specification), as @Eric explains in its answer.

+19
Jan 08 '13 at 22:08
source share

Richard Touver's answer is excellent, but I thought I would add a little to it.

As other answers pointed out, the reason for this behavior is (1) zero, convertible to any enumeration, and, obviously, for an object, and (2) any type of enumeration is more specific for this enumeration, therefore the method that accepts the enumeration is therefore selected using permission overloading as the best method. Point two - I hope that I myself will explain, but what explains the first point?

Firstly, there is an unsuccessful deviation from the specification. The specification states that any literal zero, i.e. The number 0 , which is actually literally displayed in the source code, can be implicitly converted to any type of enumeration. The compiler actually implements that any constant zero can be converted in this way. The reason for this is due to an error due to which the compiler sometimes allows constant zeros, and sometimes not, in a strange and inconsistent manner. The easiest way to solve the problem was to consistently allow constant zeros. You can read more about this here:

http://blogs.msdn.com/b/ericlippert/archive/2006/03/28/the-root-of-all-evil-part-one.aspx

Secondly, the reason for converting zeros to any enumeration is that it is always possible to clear the β€œflag” of an enumeration. Good programming practice is that each enumeration of flags has a value of None, which is zero, but this is a rule, not a requirement. C # 1.0 designers thought it looked weird that you might have to say

 for (MyFlags f = (MyFlags)0; ... 

to initialize local. My personal opinion is that this solution caused more problems than it cost, both from the point of view of sadness over the aforementioned error, and from the point of view of the oddities that it introduces in detecting the overload that you found.

Finally, the designers of the designers could understand that this would be a problem in the first place, and made overload signatures so that the developer could clearly decide which ctor to call without inserting a role. Unfortunately, this is a rather obscure problem, and therefore many designers are not aware of this. Hope anyone reading this will not make a mistake; Do not create ambiguities between an object and any enumeration if you intend for the two overrides to have different semantics .

+12
Jan 09 '13 at 15:09
source share

This, apparently, is known behavior and affects any function overloads where there is both an enumeration and an object type. I don’t understand all this, but Eric Lippert pretty well summed up his blog.

+1
Jan 08
source share

This is because integer literal 0 has an implicit conversion to any type of enumeration. The C # specification reads:

6.1.3. Implicit enumeration conversions

Implicit conversion of enumerations allows a decimal literal 0 to be converted to any type of enumeration and any type with a null value, the base type is an enumerated type. In the latter case, the conversion is evaluated by converting the result to a base type of enumeration and wrapping.

As a result, the most specific overload in this case is SqlParameter(string, DbType) .

This does not apply to other int values, so the constructor of SqlParameter(string, object) is the most specific.

+1
Jan 08
source share

When resolving a type for an overloaded method, C # selects the most specific option. The SqlParameter class has two constructors that take exactly two arguments, SqlParameter(String, SqlDbType) and SqlParameter(String, Object) . When you provide a literal of 0 , it can be interpreted as an object or as SqlDbType. Since SqlDbType is more specific than Object, this is assumed to be the intention.

You can learn more about overload resolution in this answer .

0
Jan 08 '13 at 21:50
source share



All Articles