编译器值类型解析和硬编码“0”整数值首先,介绍一些背景知识。阅读此处发布的问题和已接受的答案,了解我的问题的具体情况。我不确定是否存在其他类似案例,但这是我所知道的唯一案例。上面的“怪癖”是我早就意识到的。直到最近我才明白其中的全部原因。Microsoft关于SqlParameter类的文档更清楚地说明了这种情况。当在值参数中指定Object时,SqlDbType是从Object的Microsoft.NETFramework类型推断出来的。使用SqlParameter构造函数的重载指定整数参数值时要小心。由于此重载采用Object类型的值,因此当值为零时,整数值必须转换为Object类型,如以下C#示例所示。Parameter=newSqlParameter("@pname",Convert.ToInt32(0));如果不执行此转换,编译器会假定您正在尝试调用SqlParameter(string,SqlDbType)构造函数重载。(emph.Supplement)我的问题是为什么编译器假设当你指定硬编码的“0”(并且只有值“0”)时,你试图指定一个枚举类型而不是整数类型?在这种情况下,它假设您声明了一个SqlDbType值,而不是0值。这是不直观的,更糟糕??的是,错误是不一致的。我有我编写的旧应用程序,这些应用程序多年来一直在调用存储过程。我将对应用程序进行更改(通常甚至与我的SQLServer类无关),发布更新,这个问题会突然破坏应用程序。当包含多个方法签名的对象包含两个相似的签名,其中一个参数是对象/整数,另一个接受枚举时,为什么编译器会被值0混淆?正如我所提到的,我从未将此视为任何其他类的任何其他构造函数或方法的问题。这是SqlParameter类独有的还是C#/.Net中的继承错误?这是因为零整数可以隐式转换为枚举:enumSqlDbType{Zero=0,One=1}classTestClass{publicTestClass(strings,objecto){System.Console.WriteLine("{0}=>TestClass(对象)",s);}publicTestClass(strings,SqlDbTypee){System.Console.WriteLine("{0}=>TestClass(EnumSqlDbType)",s);}}//这是完全有效的:SqlDbTypevalid=0;//虽然这不是:SqlDbTypeohNoYouDont=1;vara1=newTestClass("0",0);//0=>TestClass(EnumSqlDbType)vara2=newTestClass("1",1);//=>1=>TestClass(object)(改编自VisualC#2008重大变更–变更12)当编译器重载SqlDbType时,0是SqlDbType和对象构造函数的Applicable函数成员,因为:存在隐式转换(section6.1)从参数的类型到相应参数的类型(SqlDbTypex=0和objectx=0都是有效的)SqlDbType参数优先于对象参数,因为更好的转换规则:如果S是T1,则C1是更好的转换。如果S是T2,则C2是更好的变换。如果存在从T1到T2的隐式转换,并且没有从T2到T1的隐式转换,则C1是更好的转换。如果存在从T2到T1的隐式转换,并且没有从T1到T2的隐式转换,则C2是更好的转换。请注意,正如@Eric在他的回答中解释的那样,在VisualC#2008(Microsoft对C#规范的实现)中,常量0的确切构成发生了变化(非常微妙)。理查德·托尔斯(RichardTowers)的回答非常好,但我想我要补充一点。正如其他答案所指出的那样,这种行为的原因是(1)nil可以转换为任何枚举,显然是一个对象,并且(2)任何枚举类型都更特定于该对象,因此采用枚举的方法是,通过重载解析来选择更好的方法。第二点我希望不言自明,但是第一点怎么解释呢?首先,不幸的是这里偏离了规范。规范说任何文字零,即实际出现在源代码中的数字0,都可以隐式转换为任何枚举类型。编译器实际上实现了任何常量零都可以这样转换。这样做的原因是因为存在错误,编译器有时会以奇怪且不一致的方式允许常量零,有时则不允许。解决该问题的最简单方法是始终允许常数零。您可以在这里详细阅读:http://blogs.msdn.com/b/ericlippert/archive/2006/03/28/the-root-of-all-evil-part-one.aspx其次,允许零转换to任何枚举的原因是为了确保“flags”枚举始终可以归零。良好的编程习惯是每个“标志”枚举都有一个“无”值,它等于零,但这是一个指南,而不是要求。C#1.0的设计者认为你可能不得不说它看起来很奇怪for(MyFlagsf=(MyFlags)0;...来初始化局部变量。我个人的观点是这个决??定带来的麻烦超过了它的价值,无论是悲痛对于上面的错误或您发现重载决议引入的怪异现象。最后,构造函数的设计者可能首先意识到这是一个问题,并独立地向开发人员明确了重载的签名决定调用哪个ctor而不必插入强制转换。不幸的是,这是一个相当晦涩的问题,所以许多设计师没有意识到这一点。希望阅读本文的任何人都不会犯同样的错误;如果您希望两个覆盖具有不同的语义,请不要在对象和任何枚举之间产生歧义。这显然是一种已知行为,将影响存在枚举和对象类型的任何函数重载。我不明白,但EricLippert总结得很好他的博客这是因为整数文字0具有到任何枚举类型的隐式转换。C#规范指出:6.1.3隐式枚举转换隐式枚举转换允许将十进制-整数-文字0转换为任何枚举类型和类型为枚举类型的任何可空类型。在后一种情况下,通过转换为基础枚举类型并包装结果来评估转换。所以在这种情况下,最具体的重载是SqlParameter(string,DbType)。这不适用于其他int值,因此SqlParameter(string,object)构造函数是最具体的。在解析重载方法的类型时,C#选择最具体的选项。SqlParameter类有两个构造函数,只有两个参数,SqlParameter(String,SqlDbType)和SqlParameter(String,Object)。当您提供文字0时,它可以被解释为Object或SqlDbType。由于SqlDbType比Object更具体,因此假定它是意图。您可以在此答案中阅读有关重载决议的更多信息。以上就是C#学习教程的全部内容:编译器值类型解析和硬编码“0”整数值。如果对你有用,需要了解更多C#学习教程,希望大家多加关注——本文来自网络合集,不代表立场,如涉及侵权,请点击有权联系管理员删除。如需转载请注明出处:
