可空类型和三元运算符:为啥是`? 10:null`禁止? [复制]

Posted

技术标签:

【中文标题】可空类型和三元运算符:为啥是`? 10:null`禁止? [复制]【英文标题】:Nullable types and the ternary operator: why is `? 10 : null` forbidden? [duplicate]可空类型和三元运算符:为什么是`? 10:null`禁止? [复制] 【发布时间】:2010-10-25 21:03:36 【问题描述】:

我刚刚遇到了一个奇怪的错误:

private bool GetBoolValue()

    //Do some logic and return true or false

然后,在另一种方法中,像这样:

int? x = GetBoolValue() ? 10 : null;

很简单,如果方法返回true,给Nullableintx赋值10。否则,将 null 分配给 nullable int。但是,编译器抱怨:

错误 1 ​​无法确定条件表达式的类型,因为int<null> 之间没有隐式转换。

我要疯了吗?

【问题讨论】:

也许这会在编译器的未来版本中得到纠正,因为 'int' 和 '' 之间确实存在隐式转换,那就是 'int?' 想象编译器足够聪明,可以说“好的,我们没有使用第一种类型 (int),所以我们将寻找另一种可以用于此表达式结果的类型。 "如果您输入“Console.WriteLine((Predicate() ? 5.6 : null).GetType().ToString());”会得到什么?你会浮起来吗?还是双倍?还是对象?还是从浮点数隐式转换的用户定义类?编译器如何知道选择哪种类型,因为它无法选择表达式中的类型? 我的意思是,最重要的是,可空类型并没有什么特别之处,只是它们有一个简写“?”句法。编译器没有一个特殊的标志告诉它一个 int?是“就像一个带有一点额外内容的 int,所以尽量使用它。” @bruno,@mquander,我个人更愿意在我做这样疯狂的事情时看到错误,如果我真的想要一个对象,那么我会做一个明确的演员表。编译器的当前行为很好:如果两种类型之间存在隐式转换(不是通过第三种猜测/推断类型),那么一切正常。如果没有隐式转换,则会出现错误。 确实,在整个 C# 中使用的微妙但重要的设计原则之一是,在根据多个备选方案决定某物的类型时,我们总是选择备选方案中最好的一个 .我们永远不会“创造”一种不在替代品中的新类型,然后选择它。我们只从您的代码中已经存在的类型中进行选择,并且只有其中一种明显优于其他类型。 【参考方案1】:

顺便说一句,C# 编译器的 Microsoft 实现实际上以一种非常微妙和有趣(对我而言)的方式错误地对条件运算符进行了类型分析。我的文章是 Type inference woes, part one (2006-05-24)。

【讨论】:

【参考方案2】:

这是因为编译器通过其第二个和第三个操作数来确定条件运算符的类型,而不是根据您将结果分配给什么。整数和空引用之间没有直接转换,编译器可以使用它来确定类型。

【讨论】:

我不同意。我认为,自然的期望是条件表达式的类型将是第二个和第三个操作数类型的最小公分母。您将结果分配到的数据类型不需要在类型解析中发挥任何作用。【参考方案3】:

编译器首先尝试计算右边的表达式:

GetBoolValue() ? 10 : null

10int 文字(不是 int?),nullnull。这两者之间没有隐式转换,因此出现错误消息。

如果将右侧表达式更改为以下表达式之一,则它会编译,因为在 int?null (#1) 之间以及 intint? (#2, #3)。

GetBoolValue() ? (int?)10 : null    // #1
GetBoolValue() ? 10 : (int?)null    // #2
GetBoolValue() ? 10 : default(int?) // #3

【讨论】:

你也可以写new int?() 或者更好的恕我直言:default(int?) 是的,编译器在将int 10 包装int? 之前需要此线索。请注意,另一种可能性是将int 10 装箱System.Int32 的基类或接口。例如GetBoolValue() ? (IFormattable)10 : null // #1BGetBoolValue() ? 10 : (IFormattable)null // #2B 就可以了。这种可能性可能是他们不使可空包装自动的原因。因为包装和装箱通常都是隐式转换。那就是 int? variable = 10;IFormattable variable = 10; 都是合法的(前者包裹,后者盒子)。 我希望我的评论是在上下文中。我的要求是在字符串为 NULL 时将 null 传递给 Oracle 表编号列。针对上面的示例,我这样管理它: cmd.Parameters.Add("2", OracleDbType.Double).Value = String.IsNullOrEmpty(pNumberChar) ? (Double?)null : Convert.ToDouble(pNumberChar);【参考方案4】:

试试这个:

int? result = condition ? 10 : default(int?);

【讨论】:

【参考方案5】:

试试这个:

int? x = GetBoolValue() ? 10 : (int?)null;

基本上发生的情况是条件运算符无法确定表达式的“返回类型”。由于编译器隐含地决定10int,因此它决定这个表达式的返回类型也应该是int。由于int 不能是null(条件运算符的第三个操作数),它会报错。

通过将null 转换为Nullable<int>,我们明确告诉编译器该表达式的返回类型应为Nullable<int>。您也可以轻松地将 10 转换为 int? 并获得相同的效果。

【讨论】:

我知道我可以解决它,我只是好奇为什么会这样? 您可以进一步缩短:int? x = GetBoolValue() ? 10 : default;【参考方案6】:

问题是三元运算符是根据您的第一个参数分配推断类型...10 在这种情况下,它是一个 int,而不是一个可为空的 int。

你可能会有更好的运气:

int? x = GetBoolValue() (int?)10 : null;

【讨论】:

【参考方案7】:

只需添加一个显式演员表。

int? x = GetBoolValue() ? 10 : (int?)null;

混淆的是三元运算符 - 第二个参数是一个整数,第三个参数也应该是一个整数,并且 null 不适合。

【讨论】:

【参考方案8】:

尝试以下方法之一:

int? x = GetBoolValue() ? (int?)10 : null;

int? x = GetBoolValue() ? 10 : (int?)null;

【讨论】:

我们可以再添加一个:int? x = GetBoolValue() ? 10 : 新的 int?();【参考方案9】:
int? x = GetBoolValue() ? 10 : (int?)null;

你看到这个的原因是你在幕后使用 Nullable,你需要告诉 C# 你的“null”是 Nullable 的一个空实例。

【讨论】:

噢!安德鲁得到了他们的第一个。 +1 给他。

以上是关于可空类型和三元运算符:为啥是`? 10:null`禁止? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥 C# 6.0 在使用 Null 传播运算符时不允许设置非 null 可空结构的属性?

为啥 C# 6.0 在使用 Null 传播运算符时不允许设置非 null 可空结构的属性?

可空类型 int?及?相关运算符

为啥在这种情况下可空类型不相等?

? 的可空类型问题:条件运算符

prisma:为啥我不能将“null”设置为可空列的默认值?