42

以下代码将无法编译:

string foo = "bar";
Object o = foo == null ? DBNull.Value : foo;

我得到:错误 1 ​​条件表达式的类型无法确定,因为 'System.DBNull' 和 'string' 之间没有隐式转换

要解决此问题,我必须执行以下操作:

string foo = "bar";
Object o = foo == null ? DBNull.Value : (Object)foo;

这个演员表似乎毫无意义,因为这当然是合法的:

string foo = "bar";
Object o = foo == null ? "gork" : foo;

在我看来,当三元分支是不同类型时,编译器不会将值自动装箱到类型对象......但是当它们是相同类型时,自动装箱是自动的。

在我看来,第一个声明应该是合法的......

谁能描述为什么编译器不允许这样做以及为什么 C# 的设计者选择这样做?我相信这在 Java 中是合法的......虽然我还没有验证这一点。

谢谢。

编辑:我要求了解为什么 Java 和 C# 以不同的方式处理这个问题,C# 中的幕后发生了什么使这无效。我知道如何使用三元,并且不是在寻找一种“更好的方式”来编写示例。我了解 C# 中三进制的规则,但我想知道为什么...

编辑(乔恩斯基特):删除“自动装箱”标签,因为此问题不涉及装箱。

4

3 回答 3

70

编译器要求第二个和第三个操作数的类型相同,或者一个可以隐式转换为另一个。在您的情况下,类型是 DBNull 和字符串,两者都不能隐式转换为另一个。将它们中的任何一个投射到对象上可以解决这个问题。

编辑:看起来它在 Java 中确实是合法的。当涉及到方法重载时,它是如何解决的,我不确定......我刚刚看过 JLS,当有两个不兼容的参考时,条件的类型是什么非常不清楚涉及的类型。C# 的工作方式有时可能更令人恼火,但 IMO 更清晰。

C# 3.0 规范的相关部分是 7.13,条件运算符:

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

  • 如果 X 和 Y 是相同的类型,那么这是条件的类型
  • 否则,如果存在从 X 到 Y 的隐式转换(第 6.1 节),而不是从 Y 到 X,则 Y 是条件表达式的类型。
  • Otherwise, if an implicit conversion (§6.1) exists from Y to X, but not from X to Y, then X is the type of the conditional expression.
  • Otherwise, no expression type can be determined, and a compile-time error occurs.
于 2008-10-14T18:35:21.180 回答
18

DBNull.Value返回类型DBNull

您希望类型为string.

虽然string可以null,但不能是DBNull.

在您的代码中,等号右侧的语句在分配给对象之前执行。

基本上,如果您使用:

[condition] ? true value : false value;

在 .Net 中,true 和 false 选项都需要隐式转换为相同的类型,然后再分配给它们。

这是 C# 如何处理类型安全的结果。例如以下是有效的:

string item = "item";

var test = item != null ? item : "BLANK";

C#3 不支持动态类型,那么什么是测试?在 C# 中,每个赋值也是一个带有返回值的语句,因此尽管var构造在 C#3 中是新的,但 equals 右侧的语句始终必须解析为单一类型。

在 C#4 及更高版本中,您可以显式支持动态类型,但我认为这在这里没有帮助。

于 2008-10-14T18:31:54.493 回答
10

By the way, your code is a special case that doesn't have to use the conditional operator at all. Instead, the null coalesce operator is more appropriate (but still requires casting):

object result = (object)foo ?? DBNull.Value;
于 2008-10-14T18:56:19.967 回答