RLH*_*RLH 18 .net c# compiler-construction
首先,有点背景.阅读此处发布的问题和已接受的答案,以了解我的问题的具体方案.我不确定是否存在其他相似的案例,但这是我所知道的唯一案例.
上面的"怪癖"是我早已意识到的.直到最近,我才明白原因的全面性.
微软关于这个SqlParameter课程的文档对这种情况有了更多的了解.
Object在value参数中指定a 时,SqlDbType将从Object的Microsoft .NET Framework类型推断出.使用
SqlParameter构造函数的此重载指定整数参数值时请小心.因为此重载采用type的值Object,所以必须在值为零时将整数值转换为Object类型,如下面的C#示例所示.
Parameter = new SqlParameter("@pname", Convert.ToInt32(0));如果不执行此转换,编译器会假定您尝试调用SqlParameter(string,SqlDbType)构造函数重载.
(emph.补充)
我的问题是为什么编译器假设当你指定硬编码"0"(并且只有值"0")时,你试图指定枚举类型而不是整数类型?在这种情况下,它假定您声明SqlDbType值,而不是值0.
这是非直观的,更糟糕的是,错误是不一致的.我有一些旧的应用程序,我已经编写了多年来调用存储过程.我将对应用程序进行更改(通常甚至与我的SQL Server类无关),发布更新,这个问题会突然中断应用程序.
当包含多个方法签名的对象包含两个相似的签名时,为什么编译器会被值0混淆,其中一个参数是对象/整数而另一个接受枚举?
正如我所提到的,我从未将其视为任何其他类的任何其他构造函数或方法的问题.这是SqlParameter该类的独特之处还是这是一个在C#/ .Net中继承的bug?
Ric*_*ers 19
这是因为零整数可以隐式转换为枚举:
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)
Run Code Online (Sandbox Code Playgroud)
(改编自Visual C#2008突破性变化 - 改变12)
当编译器执行重载解析0是一个适用功能部件两者的SqlDbType和object构造,因为:
从参数的类型到相应参数的类型存在隐式转换(第6.1节)
(这两个SqlDbType x = 0和object x = 0有效)
由于更好的转换规则,SqlDbType参数优于object参数:
T1且T2是相同的类型,则转换都不会更好.
object并且SqlDbType不是同一类型S是T1,C1是更好的转换.
0 不是 objectS是T2,C2是更好的转换.
0 不是一个 SqlDbTypeT1到T2存在,从没有隐式转换T2到T1存在,C1是更好的转换.
object到SqlDbType存在T2到T1存在,从没有隐式转换T1到T2存在,C2是更好的转换.
SqlDbType为object存在,因此SqlDbType转换效果更好请注意,正如@Eric在他的回答中解释的那样,在Visual C#2008(微软的C#规范的实现)中,究竟构成常量0的内容已经(非常微妙地)发生了变化.
Eri*_*ert 16
理查德·托尔斯的回答非常好,但我想我会补充一点.
正如其他答案所指出的那样,行为的原因是(1)零可以转换为任何枚举,显然是对象,(2)任何枚举类型都更具体于该对象,因此采用枚举的方法是因此,通过重载分辨率选择更好的方法.第二点是我希望不言自明,但是什么解释了第一点?
首先,这里有一个不幸的偏离规范.规范说任何文字零,即0实际字面上出现在源代码中的数字,可以隐式转换为任何枚举类型.编译器实际上实现了可以如此转换任何常数零.原因是因为编译器有时会以奇怪和不一致的方式允许常量为零而有时不允许常量为零的错误.解决问题的最简单方法是始终允许恒定的零.你可以在这里详细阅读:
http://blogs.msdn.com/b/ericlippert/archive/2006/03/28/the-root-of-all-evil-part-one.aspx
其次,允许零转换为任何枚举的原因是为了确保始终可以将"标志"枚举归零.良好的编程习惯是每个"标志"枚举都有一个值"无",它等于零,但这是一个指导,而不是一个要求.C#1.0的设计者认为你可能不得不说它看起来很奇怪
for (MyFlags f = (MyFlags)0; ...
Run Code Online (Sandbox Code Playgroud)
初始化本地.我个人的观点是,这个决定造成了比它值得更多的麻烦,无论是对上述错误的悲痛还是你在发现的重载决议中引入的奇怪之处.
最后,构造函数的设计者可能已经意识到这首先是一个问题,并且使得重载的签名使得开发人员可以清楚地决定调用哪个ctor而不必插入强制转换.不幸的是,这是一个相当模糊的问题,所以很多设计师都没有意识到这一点.希望任何阅读此内容的人都不会犯同样的错误; 如果您希望两个覆盖具有不同的语义,请不要在对象和任何枚举之间创建歧义.