【问题标题】:Why is this code invalid in C#?为什么这段代码在 C# 中无效?
【发布时间】:2010-09-17 03:59:50
【问题描述】:

以下代码将无法编译:

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# 中的三进制规则,但我想知道为什么...

编辑(Jon Skeet):删除了“自动装箱”标签,因为此问题不涉及装箱。

【问题讨论】:

  • 如果像这里这样使用更有效的测试 DBNulls 的方法会更容易调试吗? :stackoverflow.com/questions/26809/…
  • 其他答案解释得很好。一个额外的花絮:即使编译成功,这里也不会涉及拳击。 String 和 DBNull 都是引用(类)类型,而不是值类型,因此字符串可以存储在对象变量中而无需装箱。
  • 问题添加到C# Tag Wiki
  • srsl,为什么有人没有想到 "int? = (xxx)? null : int; variant?? 我的意思是这是一个实际和常见的问题,那我为什么要这么做写 ...(int?)null : int; 这太疯狂了!

标签: c# nullable conditional-operator dbnull


【解决方案1】:

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

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

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

第二个和第三个操作数 ?:操作员控制的类型 条件表达式。设 X 和 Y 为 第二个和第三个的类型 操作数。那么,

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

【讨论】:

    【解决方案2】:

    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 中是新的,但等号右侧的语句始终必须解析为单一类型。

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

    【讨论】:

    • 你不明白这个问题。我想要类型对象,而不是字符串。我知道三元是如何工作的。我在问为什么编译器不能自动将三元中的两种不同类型装箱到一个对象中。
    • 我认为他给出了正确的答案,最值得注意的是他这部分:“在.Net 中,true 和 false 选项必须是相同的类型,然后再分配给它们。这是结果C# 如何处理类型安全。”
    • @Keith:它们实际上不必是同一类型。只有一种类型可以隐式转换为另一种类型。 @mmattax:这里根本没有拳击比赛。 DBNull 和 String 都不是值类型。
    • 装箱确实发生在值类型被转换为对象类型时。而且我不是在询问规则...我想知道为什么会发生这种情况...
    • mmattax:没有涉及值类型,所以没有装箱。在使用术语时,它有助于准确。 (它也不叫三元运算符——它是条件运算符。它恰好是 a 三元运算符——也是 C# 中唯一的一个——但这不是它的名字。)
    【解决方案3】:

    顺便说一句,您的代码是一种特殊情况,根本不需要使用条件运算符。相反,空合并运算符更合适(但仍需要强制转换):

    object result = (object)foo ?? DBNull.Value;
    

    【讨论】:

      猜你喜欢
      • 2012-12-03
      • 1970-01-01
      • 2014-02-02
      • 1970-01-01
      • 1970-01-01
      • 2012-11-04
      • 2010-11-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多