C#?:有条件的运算符

我有这个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而第二个则不会。 这两者有什么区别?

更新:这个问题是我的博客在2010年5月27日的主题 。 感谢您的好问题!

这里有很多令人困惑的答案。 让我试着精确地回答你的问题。 我们来简化它:

 object value = whatever; bool condition = something; decimal result = (decimal)(condition ? value : 0); 

编译器如何解释最后一行? 编译器面临的问题是条件expression式types必须对于两个分支都是一致的 ; 语言规则不允许你在一个分支上返回对象而在另一个分支上返回int。 选项是对象和整数。 每个int都可以转换为对象,但不是每个对象都可以转换为int,所以编译器会select对象。 所以这是一样的

 decimal result = (decimal)(condition ? (object)value : (object)0); 

因此返回的零是一个盒装的int。

然后您将int解包为十进制。 将boxed int解除为十进制是非法的。 为什么,请参阅我关于这个主题的博客文章:

表示和身份

基本上,你的问题是,你的行为好像是十进制数字分配,就像这样:

 decimal result = condition ? (decimal)value : (decimal)0; 

但是,正如我们所看到的,那不是什么

 decimal result = (decimal)(condition ? value : 0); 

手段。 这意味着“将两种select变成对象,然后取消结果对象”。

不同之处在于编译器无法确定ObjectInt32之间良好匹配的数据types。

你可以显式地将int值转换为object ,以便在第二个和第三个操作数中获得相同的数据types,以便编译,但是这意味着你正在装箱和拆箱:

 result = (decimal)(valueFromDatabase != DBNull.value ? valueFromDatabase : (object)0); 

这将编译,但不能运行。 您必须将十进制值作为十进制值装箱:

 result = (decimal)(valueFromDatabase != DBNull.value ? valueFromDatabase : (object)0M); 

运算符的types将是对象,如果结果必须是0,它将被隐式装箱。 但0字面默认是inttypes,所以你box int。 但是,如果将显式转换为十进制,则尝试对其进行取消操作,这是不允许的(盒子types必须与您要转换回来的相同)。 这就是为什么你可以得到例外。

这里是C#规范的摘录:

?:操作符的第二个和第三个操作数控制条件expression式的types。 设X和Y是第二个和第三个操作数的types。 然后,

  • 如果X和Y是相同的types,那么这是条件expression式的types。
  • 否则,如果存在从X到Y而不是从Y到X的隐式转换(第6.1节),则Y是条件expression式的types。
  • 否则,如果存在从Y到X而不是从X到Y的隐式转换(第6.1节),则X是条件expression式的types。
  • 否则,不能确定expression式types,并发生编译时错误。

你的路线应该是:

 result = valueFromDatabase != DBNull.value ? (decimal)valueFromDatabase : 0m; 

0m是零的十进制常量

条件运算符的两个部分都应计算为相同的数据types

x:y部分需要一个通用types,数据库的值可能是某种浮点数,0是一个int。 这发生在铸造到十进制之前。 试试“:0.0”或“:0D”。

除非我错了(这是非常可能的),它实际上是造成这个exception的0,这是假设一个文字的types(疯狂),所以你需要指定0米,而不是0。

有关更多信息,请参阅MSDN 。

编译器有两种不同的types来决定(在编译时)哪一个要转换成十进制。 这是不行的。

如果你把两者结合起来,

 result = (decimal)(valueFromDatabase != DBNull.Value ? (decimal)valueFromDatabase : (decimal)0); 

至less,类似的情况下,为我的参数。