C#?:条件运算符

时间:2009-07-23 13:28:04

标签: c# c#-2.0 conditional-operator .net

我有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}}。 这两者有什么区别?

8 个答案:

答案 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解包为十进制是非法的。出于这个原因,请参阅我关于该主题的博客文章:

Representation and Identity

基本上,你的问题是你表现得就好像分配了十进制数,就像这样:

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

但正如我们所见,那不是什么

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

装置。这意味着“将两个替代品都放入对象,然后取消生成对象”。

答案 1 :(得分:12)

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

您可以显式地将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是第二个和第三个操作数的类型。然后,

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

答案 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);

至少,类似的情况会转化为我的参数。