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变成对象,然后取消结果对象”。
不同之处在于编译器无法确定Object
和Int32
之间良好匹配的数据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,类似的情况下,为我的参数。