空值如何?真的是一个字符串吗?

发布于 2024-10-08 00:23:08 字数 265 浏览 3 评论 0原文

既然 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

緦唸λ蓇 2024-10-15 00:23:09

有趣的是,使用 Reflector 来检查生成的内容,以下代码:

string b = null + true;
Console.WriteLine(b);

由编译器转换为:

Console.WriteLine(true);

我必须说,这种“优化”背后的推理有点奇怪,并且与我期望的运算符选择不押韵。

另外,以下代码:

var b = null + true; 
var sb = new StringBuilder(b);

被转换为

string b = true; 
StringBuilder sb = new StringBuilder(b);

where string b = true; 实际上不被编译器接受。

Interestingly, using Reflector to inspect what is generated, the following code:

string b = null + true;
Console.WriteLine(b);

is transformed into this by the compiler:

Console.WriteLine(true);

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:

var b = null + true; 
var sb = new StringBuilder(b);

is transformed into

string b = true; 
StringBuilder sb = new StringBuilder(b);

where string b = true; is actually not accepted by the compiler.

完美的未来在梦里 2024-10-15 00:23:09

null 将被转换为 null 字符串,并且存在从 bool 到 string 的隐式转换器,因此 true 将被转换为字符串,然后 +将应用运算符:就像: string str = "" + true.ToString();

如果你用 Ildasm 检查它:

string str = null + true;

如下所示:

.locals init ([0] string str)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  box        [mscorlib]System.Boolean
  IL_0007:  call       string [mscorlib]System.String::Concat(object)
  IL_000c:  stloc.0

null will be cast to null string, and there is implicit converter from bool to string so the true 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:

.locals init ([0] string str)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  box        [mscorlib]System.Boolean
  IL_0007:  call       string [mscorlib]System.String::Concat(object)
  IL_000c:  stloc.0
橘味果▽酱 2024-10-15 00:23:09
var b = (null + DateTime.Now); // String
var b = (null + 1);            // System.Nullable<Int32> | same with System.Single, System.Double, System.Decimal, System.TimeSpan etc
var b = (null + new Object()); // String | same with any ref type

疯狂的??不,这背后一定有原因。

有人打电话给Eric Lippert...

var b = (null + DateTime.Now); // String
var b = (null + 1);            // System.Nullable<Int32> | same with System.Single, System.Double, System.Decimal, System.TimeSpan etc
var b = (null + new Object()); // String | same with any ref type

Crazy?? No, there must be a reason behind it.

Someone call Eric Lippert...

浸婚纱 2024-10-15 00:23:09

这样做的原因是方便(连接字符串是一项常见任务)。

正如 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.

雨落□心尘 2024-10-15 00:23:08

尽管这看起来很奇怪,但它只是遵循 C# 语言规范的规则。

从第 7.3.4 节:

x op y 形式的运算,其中 op 是可重载二元运算符,x 是 X 类型的表达式,y 是 Y 类型的表达式,处理如下:

  • 确定 X 和 Y 为运算运算符 op(x, y) 提供的候选用户定义运算符集合。该集合由 X 提供的候选运算符和 Y 提供的候选运算符的并集组成,每个运算符均使用第 7.3.5 节的规则确定。如果 X 和 Y 是相同类型,或者 X 和 Y 派生自共同的基本类型,则共享候选运算符仅在组合集中出现一次。
  • 如果候选用户定义运算符集合不为空,则这将成为该操作的候选运算符集合。否则,预定义的二元运算符 op 实现(包括其提升形式)将成为该操作的候选运算符集。给定运算符的预定义实现在该运算符的描述中指定(第 7.8 节到第 7.12 节)。
  • 第 7.5.3 节的重载决策规则应用于候选运算符集,以选择相对于参数列表 (x, y) 的最佳运算符,并且该运算符成为重载决策过程的结果。如果重载决策无法选择单个最佳运算符,则会发生绑定时错误。

那么,让我们依次讨论一下。

X 在这里是空类型——或者根本不是类型,如果你想这样想的话。它没有提供任何候选人。 Y 是 bool,它不提供任何用户定义的 + 运算符。所以第一步没有找到用户定义的运算符。

然后,编译器继续处理第二个要点,查看预定义的二元运算符 + 实现及其提升形式。这些列在规范的第 7.8.4 节中。

如果您查看这些预定义的运算符,唯一适用的是字符串运算符+(string x, object y)。因此候选集只有一个条目。这使得最后的要点非常简单......重载解析选择该运算符,给出 string 的整体表达式类型。

一个有趣的一点是,即使在未提及的类型上有其他可用的用户定义运算符,这种情况也会发生。例如:

// Foo defined Foo operator+(Foo foo, bool b)
Foo f = null;
Foo g = f + true;

这很好,但它不用于空文字,因为编译器不知道在 Foo 中查找。它只知道考虑 string 因为它是规范中明确列出的预定义运算符。 (事实上​​,它不是由字符串类型定义的运算符...1)这意味着这将无法编译:

// Error: Cannot implicitly convert type 'string' to 'Foo'
Foo f = null + true;

其他第二操作数类型将使用其他一些当然是运算符:

var x = null + 0; // x is Nullable<int>
var y = null + 0L; // y is Nullable<long>
var z = null + DayOfWeek.Sunday; // z is Nullable<DayOfWeek>

1 您可能想知道为什么没有字符串 + 运算符。这是一个合理的问题,我只是猜测答案,但请考虑这个表达式:

string x = a + b + c + d;

如果 string 在 C# 编译器中没有特殊大小写,这最终会是同样有效:

string tmp0 = (a + b);
string tmp1 = tmp0 + c;
string x = tmp1 + d;

这样就创建了两个不必要的中间字符串。然而,由于编译器内部有特殊支持,它实际上能够将上述内容编译为:

string x = string.Concat(a, b, c, d);

它可以创建一个长度完全正确的单个字符串,将所有数据复制一次。好的。

Bizarre as this may seem, it's simply following the rules from the C# language spec.

From section 7.3.4:

An operation of the form x op y, where op is an overloadable binary operator, x is an expression of type X, and y is an expression of type Y, is processed as follows:

  • The set of candidate user-defined operators provided by X and Y for the operation operator op(x, y) is determined. The set consists of the union of the candidate operators provided by X and the candidate operators provided by Y, each determined using the rules of §7.3.5. If X and Y are the same type, or if X and Y are derived from a common base type, then shared candidate operators only occur in the combined set once.
  • If the set of candidate user-defined operators is not empty, then this becomes the set of candidate operators for the operation. Otherwise, the predefined binary operator op implementations, including their lifted forms, become the set of candidate operators for the operation. The predefined implementations of a given operator are specified in the description of the operator (§7.8 through §7.12).
  • The overload resolution rules of §7.5.3 are applied to the set of candidate operators to select the best operator with respect to the argument list (x, y), and this operator becomes the result of the overload resolution process. If overload resolution fails to select a single best operator, a binding-time error occurs.

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 of string.

One interesting point is that this will occur even if there are other user-defined operators available on unmentioned types. For example:

// Foo defined Foo operator+(Foo foo, bool b)
Foo f = null;
Foo g = f + true;

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 consider string 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:

// Error: Cannot implicitly convert type 'string' to 'Foo'
Foo f = null + true;

Other second-operand types will use some other operators, of course:

var x = null + 0; // x is Nullable<int>
var y = null + 0L; // y is Nullable<long>
var z = null + DayOfWeek.Sunday; // z is Nullable<DayOfWeek>

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:

string x = a + b + c + d;

If string had no special-casing in the C# compiler, this would end up as effectively:

string tmp0 = (a + b);
string tmp1 = tmp0 + c;
string x = tmp1 + d;

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:

string x = string.Concat(a, b, c, d);

which can create just a single string of exactly the right length, copying all the data exactly once. Nice.

梦幻的心爱 2024-10-15 00:23:08

原因是一旦引入 +,C# 运算符绑定规则就会发挥作用。它将考虑可用的+ 运算符集并选择最佳重载。这些运算符之一如下所示。

string operator +(string x, object y)

此重载与表达式 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 following

string operator +(string x, object y)

This 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 .

別甾虛僞 2024-10-15 00:23:08

编译器首先寻找可以采用空参数的运算符+()。所有标准值类型都不符合条件,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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文