C# ? : 运算符

4

请问有人能解释一下以下编译器问题:

错误:条件表达式的类型无法确定,因为在'string'和'int'之间没有隐式转换

// WORKS
string text = string.Format(
    "the id is {0}", _Obj.Id.ToString());

// WORKS, without implicit conversion <<<
string text = string.Format(
    "the id is {0}", _Obj.Id);

// WORKS
string text = string.Format(
    "the id is {0}", (_Obj == null) ? "unknown" : _Obj.Id.ToString());

// NO WAY <<<
string text = string.Format(
    "the id is {0}", (_Obj == null) ? "unknown" : _Obj.Id);

在最后一个示例中,也没有隐式转换。

不好意思,我的答案在下面...:0 - UpTheCreek
8个回答

9
问题与您使用的 string.Format 无关。问题在于这个表达式:
(_Obj == null) ? "unknown" : _Obj.Id

编译器无法确定此表达式的类型,因为 intstring 之间没有隐式转换。您已经找到了解决方案 - 调用 ToString 意味着该表达式在任何情况下都返回一个 string。另一种修复方法是(但由于装箱而在这种情况下略微低效),明确告诉编译器如何执行转换。例如,您可以使用显式转换为 object
(_Obj == null) ? "unknown" : (object)_Obj.Id

你的第二个例子可以不需要显式转换就能正常工作,因为string.Format期望一个object,并且从intobject有一个隐式转换。

我经常使用string.Format,因为它不需要我使用ToString(),它会隐式地执行。由于String.Format接受所有类型的参数,编译器可能会将结果装箱为Object。 - serhio
3
如果没有ToString,就必须将其强制转换为对象,因为那是唯一的共同基类。编译器不会自动为您执行此操作。理论上,每当y和z在“x? Y:Z”中不兼容时,编译器都可以始终将其转换为对象,但这样做会导致即使在出现错误的情况下,代码仍然可以编译。编译器正在尝试警告您:“嘿,这些对象是不同类型的,您确定要这样做吗?”如果您真的想这样做,仍然可以,但您需要告诉编译器:“是的,我知道我在做什么,只需转换为对象-没问题”。 - Mark Byers
我理解,然而这不是一个警告,而是一个错误。我更希望这是一个警告,并将我评估为“对象”。 - serhio
@serhio:我认为出错是好事,因为当唯一的共同基类是object时,通常它就是个错误。你为什么想把它变成警告呢?写出会产生警告的代码会让你感到满意吗?像你已经做的那样,加上调用ToString()不是更好吗? - Mark Byers


2
这个表达式的类型是什么?
(_Obj == null) ? "unknown" : _Obj.Id

2
只需看表达式即可。如果编译器无法确定使用哪个重载,它就不能编译它。 - György Andrasek
@serhio:那是无关的。条件表达式 q ? x : y 要求 xy 具有兼容的类型。stringint 不兼容。 - Joren
@Joren:你所说的“兼容”是什么意思?两者都可以装箱为对象。 - serhio
1
彼此兼容。它们都与“对象”兼容,但编译器并不关心这一点。 - Joren
@Joren:“编译器并不关心这个。”为什么?它可能会关心。 - serhio

1

我认为你需要从MSDN阅读这个内容: 条件运算符

特别是这部分:

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

如果X和Y是相同的类型,则此为条件表达式的类型。否则,如果从X到Y存在隐式转换(第6.1节),但从Y到X不存在,则Y是条件表达式的类型。否则,如果从Y到X存在隐式转换(第6.1节),但从X到Y不存在,则X是条件表达式的类型。否则,无法确定任何表达式类型,并出现编译时错误。


是的,但是...我更喜欢:否则,条件表达式的类型是object :) - serhio

0

最后一个能够工作,因为string.format将接受(字符串, 对象)

第一个无法工作,因为? :运算符需要匹配的类型。


我认为这个限制是不必要的。显然微软不像我这样想 :) - serhio
2
@serhio:这不是必需的,而是一种设计选择。要求条件运算符的操作数明确确定类型的好处是可以消除代码分析中的困难和昂贵问题。缺点是用户必须确保运算符具有一致可推断的类型,不依赖于表达式的上下文。设计选择始终是找到平衡冲突优先级的妥协结果。 - Eric Lippert

0
"the id is {0}", (_Obj == null) ? "unknown" : _Obj.Id

编译器必须选择使用字符串类型或整数(猜测),默认情况下它们不能互换(没有隐式转换)。
string text = string.Format("the id is {0}", _Obj.Id)

string.Format 接受 object 作为参数,因此将 Id(整数)转换为 object 没有问题。


0

问题在于

// WORKS, without implicit conversion 
string text = string.Format( 
    "the id is {0}", _Obj.Id); 

// NO WAY 
string text = string.Format( 
    "the id is {0}", (_Obj == null) ? "unknown" : _Obj.Id); 

不是一样的!

尝试思考这个术语

(_Obj == null) ? "unknown" : _Obj.Id);

作为

function int Eval(object obj)
{
  if (obj == null)
  {
    return "unknown";
  }
  else
  {
    return "1";
  }
}

显然这并不起作用。因此,整个问题与 string.format 无关。


函数 object Eval(object obj) - serhio

-1
在第一种情况下(不起作用的情况),如果 _Obj == null,则返回一个 string,否则返回一个 int。当然,这会导致问题,因为在这种情况下,您尝试将一个 int 分配给 string text

很抱歉,那是不正确的 - 问题在于条件表达式必须解析为单一类型,而不是两种不同的类型。这与将String.Format的输出分配给字符串文本无关。 - razlebe
最后一个示例是关于 string.Format 的,与 ?: 运算符无关。仅因为 ?: 指定两个参数必须具有相同的类型或者它们之间存在隐式转换,并不意味着 string.Format 应该因为同样的原因而失败。string.Format 返回一个字符串,而不受参数的影响,将结果分配给字符串文本也没有问题。在 ?: 的情况下,返回的是一个字符串或整数,将其分配给字符串文本当然会生成编译器异常。 - Øyvind Bråthen
@sgreeve - 我从未声称过它是这样的。我解释了为什么 ?: 情况(第一种情况)失败了,而不是最后一种情况没有失败。 - Øyvind Bråthen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接