我有C#2.0源代码的摘录:
object valueFromDatabase;
decimal result;
valueFromDatabase = DBNull.Value;
result = (decimal)(valueFromDatabase != DBNull.Value ? valueFromDatabase : 0);
result = (valueFromDatabase != DBNull.Value ? (decimal)valueFromDatabase : (decimal)0);
第一个结果评估会抛出InvalidCastException
,而第二个结果评估不会抛出{{1}}。
这两者有什么区别?
答案 0 :(得分:101)
更新:这个问题是the subject of my blog on May 27th 2010。谢谢你提出的好问题!
这里有很多令人困惑的答案。让我试着准确回答你的问题。让我们简化一下:
object value = whatever;
bool condition = something;
decimal result = (decimal)(condition ? value : 0);
编译器如何解释最后一行?编译器面临的问题是条件表达式的类型必须对两个分支都是一致的;语言规则不允许您在一个分支上返回对象而在另一个分支上返回int。选择是object和int。每个int都可以转换为object,但并非每个对象都可以转换为int,因此编译器会选择对象。因此,这与
相同decimal result = (decimal)(condition ? (object)value : (object)0);
因此返回的零是一个盒装的int。
然后将int解包为十进制。将盒装int解包为十进制是非法的。出于这个原因,请参阅我关于该主题的博客文章:
基本上,你的问题是你表现得就好像分配了十进制数,就像这样:
decimal result = condition ? (decimal)value : (decimal)0;
但正如我们所见,那不是什么
decimal result = (decimal)(condition ? value : 0);
装置。这意味着“将两个替代品都放入对象,然后取消生成对象”。
答案 1 :(得分:12)
不同之处在于编译器无法确定Object
和Int32
之间匹配良好的数据类型。
您可以显式地将int
值转换为object
以在第二个和第三个操作数中获取相同的数据类型以便编译,但是couse意味着您正在装箱并取消装箱值:
result = (decimal)(valueFromDatabase != DBNull.value ? valueFromDatabase : (object)0);
那将编译,但不会运行。您必须将十进制值作为小数值包装到unbox:
result = (decimal)(valueFromDatabase != DBNull.value ? valueFromDatabase : (object)0M);
答案 2 :(得分:5)
运算符的类型将是对象,如果结果必须为0,则将隐式装箱。但是默认情况下0 literal是int类型所以你选择int。但是如果使用显式强制转换为十进制,则尝试将其取消包装(这是不允许的)(盒装类型必须与您强制转换的一样)。这就是你可以获得例外的原因。
以下是C#规范的摘录:
?:运算符的第二个和第三个操作数控制条件表达式的类型。设X和Y是第二个和第三个操作数的类型。然后,
答案 3 :(得分:4)
你的行应该是:
result = valueFromDatabase != DBNull.value ? (decimal)valueFromDatabase : 0m;
0m 是零
的十进制常量条件运算符的两个部分都应该评估为相同的数据类型
答案 4 :(得分:3)
x:y部分需要一个通用类型,数据库的值可能是某种浮点数而0是一个int。这是在转换为十进制之前发生的。尝试“:0.0”或“:0D”。
答案 5 :(得分:2)
除非我弄错了(这是非常可能的),它实际上是导致异常的0,这是由于(假设)文字的类型而需要指定0m而不是0才是.NET(疯狂)。
有关详细信息,请参阅MSDN。
答案 6 :(得分:0)
编译器有两种不同的类型(在编译时)决定将哪一种转换为十进制。这是做不到的。
答案 7 :(得分:-1)
如果你将两者结合起来,你的答案就会奏效:
result = (decimal)(valueFromDatabase != DBNull.Value ? (decimal)valueFromDatabase : (decimal)0);
至少,类似的情况会转化为我的参数。