C# 使用条件/三元(“?:”)运算符进行强制转换

C# 使用条件/三元(“?:”)运算符进行强制转换,c#,conditional-operator,C#,Conditional Operator,我有以下C#源代码摘录: object valueFromDatabase; decimal result; valueFromDatabase = DBNull.Value; result = (decimal)(valueFromDatabase != DBNull.Value ? valueFromDatabase : 0); result = (valueFromDatabase != DBNull.Value ? (decimal)valueFromDatabase : (decim

我有以下C#源代码摘录:

object valueFromDatabase;
decimal result;
valueFromDatabase = DBNull.Value;

result = (decimal)(valueFromDatabase != DBNull.Value ? valueFromDatabase : 0);
result = (valueFromDatabase != DBNull.Value ? (decimal)valueFromDatabase : (decimal)0);
第一个结果求值抛出一个
InvalidCastException
,而第二个结果求值没有抛出。
这两者之间有什么区别?

区别在于编译器无法确定
对象
Int32
之间的数据类型是否匹配良好

您可以显式地将
int
值强制转换为
object
,以在第二个和第三个操作数中获得相同的数据类型,以便进行编译,但couse的数据类型表示正在装箱和取消装箱该值:

result = (decimal)(valueFromDatabase != DBNull.value ? valueFromDatabase : (object)0);
它将编译,但不会运行。必须将一个十进制值作为十进制值取消装箱:

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

x:y部分需要一个公共类型,数据库的值可能是某种浮点,0是一个int。这发生在转换为十进制之前。Try“:0.0”或“:0D”。

运算符的类型将为object,如果结果必须为0,则将隐式装箱。但默认情况下,0 literal具有int类型,因此您可以将int设置为box。但使用显式转换为decimal时,您会尝试将其取消装箱,这是不允许的(装箱类型必须与您转换回的类型保持一致)。这就是为什么您可以得到异常

以下是C#规范的摘录:

?运算符的第二个和第三个操作数控制条件表达式的类型。设X和Y是第二个和第三个操作数的类型。那么

  • 如果X和Y是同一类型,则这是条件表达式的类型
  • 否则,如果存在从X到Y的隐式转换(§6.1),但不存在从Y到X的隐式转换,则Y是 条件表达式
  • 否则,如果存在从Y到X的隐式转换(§6.1),但不存在从X到Y的隐式转换,则X是 条件表达式
  • 否则,无法确定表达式类型,并发生编译时错误
除非我弄错了(这很有可能),否则导致异常的实际上是0,这取决于.NET(疯狂地)假设了文本的类型,因此您需要指定0m,而不仅仅是0


有关更多信息,请参阅。

编译器有两种不同的类型可决定(在编译时)将哪种类型转换为十进制。这是不行的。

您的线路应该是:

result = valueFromDatabase != DBNull.value ? (decimal)valueFromDatabase : 0m;
0m是零的十进制常数

条件运算符的两个部分应计算为相同的数据类型更新:此问题是。谢谢你的提问

这里有很多非常令人困惑的答案。让我试着准确地回答你的问题。让我们简化一下:

object value = whatever;
bool condition = something;
decimal result = (decimal)(condition ? value : 0);
编译器如何解释最后一行?编译器面临的问题是,两个分支的条件表达式的类型必须一致;语言规则不允许在一个分支上返回object,在另一个分支上返回int。选项是object和int。每个int都可以转换为object,但不是每个object都可以转换为int,因此编译器选择object。因此,这与

decimal result = (decimal)(condition ? (object)value : (object)0);
因此,返回的零是一个装箱整数

然后将int解装箱到decimal。将已装箱的整数解装箱为十进制是非法的。有关原因,请参阅我的博客文章:

基本上,您的问题是,您的行为就像十进制的转换是分布式的,如下所示:

decimal result = condition ? (decimal)value : (decimal)0;
但正如我们所看到的,这不是什么

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

意味着。这意味着“将两个备选方案都制作成对象,然后取消对结果对象的装箱”。

如果将这两个选项组合在一起,您的答案将有效:

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

至少,对于我来说,类似的情况也会转化为一个参数。

我想澄清的是,在第一个参数中,计算结果为零,而不是DBNull.value。为什么将0强制转换为十进制会引发异常?数据库中的值实际上与编译器无关。变量的类型是Object,双精度值不会比Int32值更匹配。正如其他人所说,它是decimal:0m类型的0文本零。这不是编译器错误,而是运行时错误。数据库通常以双精度形式返回十进制值,因此这是您试图匹配的类型。但是,如果运行时类型为decimal,则继续使用M.:),这很可能会编译成与第二行完全相同的代码。可能是这样,但值得指出的是,对于零isC规范,十进制常量是什么:“装箱转换允许将值类型隐式转换为引用类型。存在从任何不可为null的值类型到对象、到System.ValueType以及到由不可为null的值类型实现的任何接口类型的装箱转换。此外,枚举类型可以转换为System.enum类型。“因此Int32和object之间存在隐式装箱转换。如果编译器无法确定类型,它将在编译时失败,而不是在运行时失败。请查看我的答案……我不明白为什么编译器会在编译错误似乎更合适的地方插入类型转换。@raj:您希望看到什么规则的实现我相信编译器应该告诉我这个规则:“语言规则不允许在一个分支上返回object,在另一个分支上返回int“而不是将int转换为object以使自己快乐。如果编译器告诉洛恩尼斯,他会发现这个语句有问题it@Raj:好的,那么编译器应该如何处理b?“”:null-在一个分支上返回字符串,在另一个分支上返回无类型的null引用。编译器应该对此抱怨吗?因为其中一方甚至没有类型,显然类型不相等。@Matthew:你如何用条件表达式生成一个合法的C#程序,从而不使用该值?这毫无意义:因为你同时强制转换了两个分支,所以初始强制转换是无用的。