空值如何?真的是一个字符串吗?
既然 true
不是字符串类型,那么 null + true
怎么会是字符串呢?
string s = true; //Cannot implicitly convert type 'bool' to 'string'
bool b = null + true; //Cannot implicitly convert type 'string' to 'bool'
这背后的原因是什么?
Since true
is not a string type, how is null + true
a string ?
string s = true; //Cannot implicitly convert type 'bool' to 'string'
bool b = null + true; //Cannot implicitly convert type 'string' to 'bool'
What is the reason behind this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
有趣的是,使用 Reflector 来检查生成的内容,以下代码:
由编译器转换为:
我必须说,这种“优化”背后的推理有点奇怪,并且与我期望的运算符选择不押韵。
另外,以下代码:
被转换为
where
string b = true;
实际上不被编译器接受。Interestingly, using Reflector to inspect what is generated, the following code:
is transformed into this by the compiler:
The reasoning behind this "optimization" is a bit weird I must say, and does not rhyme with the operator selection I would expect.
Also, the following code:
is transformed into
where
string b = true;
is actually not accepted by the compiler.null
将被转换为 null 字符串,并且存在从 bool 到 string 的隐式转换器,因此true
将被转换为字符串,然后+
将应用运算符:就像: string str = "" + true.ToString();如果你用 Ildasm 检查它:
string str = null + true;
如下所示:
null
will be cast to null string, and there is implicit converter from bool to string so thetrue
will be cast to string and then,+
operator will be applied: it's like: string str = "" + true.ToString();if you check it with Ildasm:
string str = null + true;
it's as bellow:
疯狂的??不,这背后一定有原因。
有人打电话给
Eric Lippert
...Crazy?? No, there must be a reason behind it.
Someone call
Eric Lippert
...这样做的原因是方便(连接字符串是一项常见任务)。
正如 BoltClock 所说,“+”运算符是在数字类型、字符串上定义的,也可以为我们自己的类型定义(运算符重载)。
如果参数类型上没有重载的“+”运算符并且它们不是数字类型,则编译器默认为字符串连接。
当您使用“+”连接时,编译器会插入对
String.Concat(...)
的调用,并且 Concat 的实现会在传递给它的每个对象上调用 ToString。The reason for this is convenience (concatenating strings is a common task).
As BoltClock said, the '+' operator is defined on numeric types, strings, and can be defined for our own types as well (operator overloading).
If there is not an overloaded '+' operator on the argument's types and they are not numeric types, the compiler defaults to string concatenation.
The compiler inserts a call to
String.Concat(...)
when you concatenate using '+', and the implementation of Concat calls ToString on each object passed into it.尽管这看起来很奇怪,但它只是遵循 C# 语言规范的规则。
从第 7.3.4 节:
那么,让我们依次讨论一下。
X 在这里是空类型——或者根本不是类型,如果你想这样想的话。它没有提供任何候选人。 Y 是
bool
,它不提供任何用户定义的+
运算符。所以第一步没有找到用户定义的运算符。然后,编译器继续处理第二个要点,查看预定义的二元运算符 + 实现及其提升形式。这些列在规范的第 7.8.4 节中。
如果您查看这些预定义的运算符,唯一适用的是
字符串运算符+(string x, object y)
。因此候选集只有一个条目。这使得最后的要点非常简单......重载解析选择该运算符,给出string
的整体表达式类型。一个有趣的一点是,即使在未提及的类型上有其他可用的用户定义运算符,这种情况也会发生。例如:
这很好,但它不用于空文字,因为编译器不知道在
Foo
中查找。它只知道考虑string
因为它是规范中明确列出的预定义运算符。 (事实上,它不是由字符串类型定义的运算符...1)这意味着这将无法编译:其他第二操作数类型将使用其他一些当然是运算符:
1 您可能想知道为什么没有字符串 + 运算符。这是一个合理的问题,我只是猜测答案,但请考虑这个表达式:
如果
string
在 C# 编译器中没有特殊大小写,这最终会是同样有效:这样就创建了两个不必要的中间字符串。然而,由于编译器内部有特殊支持,它实际上能够将上述内容编译为:
它可以创建一个长度完全正确的单个字符串,将所有数据复制一次。好的。
Bizarre as this may seem, it's simply following the rules from the C# language spec.
From section 7.3.4:
So, let's walk through this in turn.
X is the null type here - or not a type at all, if you want to think of it that way. It's not providing any candidates. Y is
bool
, which doesn't provide any user-defined+
operators. So the first step finds no user-defined operators.The compiler then moves on to the second bullet point, looking through the predefined binary operator + implementations and their lifted forms. These are listing in section 7.8.4 of the spec.
If you look through those predefined operators, the only one which is applicable is
string operator +(string x, object y)
. So the candidate set has a single entry. That makes the final bullet point very simple... overload resolution picks that operator, giving an overall expression type ofstring
.One interesting point is that this will occur even if there are other user-defined operators available on unmentioned types. For example:
That's fine, but it's not used for a null literal, because the compiler doesn't know to look in
Foo
. It only knows to considerstring
because it's a predefined operator explicitly listed in the spec. (In fact, it's not an operator defined by the string type... 1) That means that this will fail to compile:Other second-operand types will use some other operators, of course:
1 You may be wondering why there isn't a string + operator. It's a reasonable question, and I'm only guessing at the answer, but consider this expression:
If
string
had no special-casing in the C# compiler, this would end up as effectively:So that's created two unnecessary intermediate strings. However, because there's special support within the compiler, it's actually able to compile the above as:
which can create just a single string of exactly the right length, copying all the data exactly once. Nice.
原因是一旦引入
+
,C# 运算符绑定规则就会发挥作用。它将考虑可用的+
运算符集并选择最佳重载。这些运算符之一如下所示。此重载与表达式
null + true
中的参数类型兼容。因此,它被选为运算符,并且基本上被计算为((string)null) + true
,其计算结果为值“True”
。C# 语言规范的第 7.7.4 节包含有关此解决方案的详细信息。
The reason why is because once you introduce the
+
then the C# operator binding rules come into play. It will consider the set of+
operators available and select the best overload. One of those operators is the followingThis overload is compatible with the argument types in the expression
null + true
. Hence it is selected as the operator and is evaluated as essentially((string)null) + true
which evaluates to the value"True"
.Section 7.7.4 of the C# language spec contains the details around this resolution .
编译器首先寻找可以采用空参数的运算符+()。所有标准值类型都不符合条件,null 对它们来说不是有效值。唯一的匹配是 System.String.operator+(),没有歧义。
该运算符的第二个参数也是一个字符串。这就是 kapooey,无法将 bool 隐式转换为 string。
The compiler goes out hunting for an operator+() that can take a null argument first. None of the standard value types qualify, null is not a valid value for them. The one and only match is System.String.operator+(), there's no ambiguity.
The 2nd argument of that operator is also a string. That goes kapooey, cannot implicitly convert bool to string.