null + true是一个字符串怎么样?

时间:2010-12-17 17:01:30

标签: c# .net string null types

由于true不是字符串类型,null + true如何成为字符串?

string s = true;  //Cannot implicitly convert type 'bool' to 'string'   
bool b = null + true; //Cannot implicitly convert type 'string' to 'bool'

这背后的原因是什么?

7 个答案:

答案 0 :(得分:149)

这看起来很奇怪,它只是遵循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是这里的null类型 - 或者根本不是类型,如果你想这样想的话。它没有提供任何候选人。 Y是bool,它不提供任何用户定义的+运算符。所以第一步找不到用户定义的运算符。

然后编译器转到第二个项目符号点,查看预定义的二元运算符+实现及其提升的表单。这些列在规范的第7.8.4节中。

如果查看这些预定义的运算符,则仅适用的运算符为string operator +(string x, object y)。所以候选集有一个条目。这使得最后一个要点非常简单......重载决策选择该运算符,使整体表达式类型为string

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

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

没关系,但它不用于null文字,因为编译器不知道要查看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);

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

答案 1 :(得分:44)

原因是因为一旦你引入了+,那么C#运算符绑定规则就会发挥作用。它将考虑可用的+运算符集并选择最佳重载。其中一个运营商是以下

string operator +(string x, object y)

此重载与表达式null + true中的参数类型兼容。因此,它被选为运算符,并且基本上被评估为((string)null) + true,其值为"True"

C#语言规范的第7.7.4节包含有关此分辨率的详细信息。

答案 2 :(得分:11)

编译器会寻找一个可以先取空参数的运算符+()。没有标准值类型限定,null不是它们的有效值。唯一匹配的是System.String.operator +(),没有歧义。

该运算符的第二个参数也是一个字符串。那就是kapooey,不能隐含地将bool转换为字符串。

答案 3 :(得分:10)

有趣的是,使用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);

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

答案 4 :(得分:8)

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

答案 5 :(得分:5)

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

正如BoltClock所说,'+'运算符是在数字类型,字符串上定义的,也可以为我们自己的类型定义(运算符重载)。

如果参数的类型上没有重载的'+'运算符且它们不是数字类型,则编译器默认为字符串连接。

当您使用'+'连接时,编译器会插入对String.Concat(...)的调用,并且Concat的实现会在传入其中的每个对象上调用ToString。

答案 6 :(得分:5)

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