Jav*_*ram 112 .net c# 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'
Run Code Online (Sandbox Code Playgroud)
这背后的原因是什么?
Jon*_*eet 148
这看起来很奇怪,它只是遵循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.7节到第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;
Run Code Online (Sandbox Code Playgroud)
这很好,但它不用于null文字,因为编译器不知道要查看Foo.它只知道要考虑string因为它是规范中明确列出的预定义运算符.(实际上,它不是由字符串类型定义的运算符... 1)这意味着这将无法编译:
// Error: Cannot implicitly convert type 'string' to 'Foo'
Foo f = null + true;
Run Code Online (Sandbox Code Playgroud)
其他第二操作数类型将使用其他一些运算符,当然:
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>
Run Code Online (Sandbox Code Playgroud)
1您可能想知道为什么没有字符串+运算符.这是一个合理的问题,我只是在猜测答案,但请考虑这个表达式:
string x = a + b + c + d;
Run Code Online (Sandbox Code Playgroud)
如果string在C#编译器中没有特殊的外壳,这将最终有效:
string tmp0 = (a + b);
string tmp1 = tmp0 + c;
string x = tmp1 + d;
Run Code Online (Sandbox Code Playgroud)
这样就创建了两个不必要的中间字符串.但是,因为编译器中有特殊的支持,它实际上能够将上面的代码编译为:
string x = string.Concat(a, b, c, d);
Run Code Online (Sandbox Code Playgroud)
它可以只创建一个完全正确长度的单个字符串,只复制一次所有数据.尼斯.
Jar*_*Par 44
原因是因为一旦你引入+了C#运算符绑定规则就会发挥作用.它将考虑+可用的运算符集并选择最佳过载.其中一个运营商如下
string operator +(string x, object y)
Run Code Online (Sandbox Code Playgroud)
此重载与表达式中的参数类型兼容null + true.因此,它被选择作为运算符并且被评估为基本上((string)null) + true评估该值"True".
C#语言规范的第7.7.4节包含有关此分辨率的详细信息.
Han*_*ant 11
编译器会寻找可以先取空参数的运算符+().没有标准值类型限定,null不是它们的有效值.唯一匹配的是System.String.operator +(),没有歧义.
该运算符的第二个参数也是一个字符串.那就是kapooey,不能隐含地将bool转换为字符串.
Pet*_*old 10
有趣的是,使用Reflector检查生成的内容,代码如下:
string b = null + true;
Console.WriteLine(b);
Run Code Online (Sandbox Code Playgroud)
由编译器转换为this:
Console.WriteLine(true);
Run Code Online (Sandbox Code Playgroud)
这个"优化"背后的原因有点奇怪,我必须说,并没有与我期望的运算符选择押韵.
另外,以下代码:
var b = null + true;
var sb = new StringBuilder(b);
Run Code Online (Sandbox Code Playgroud)
变成了
string b = true;
StringBuilder sb = new StringBuilder(b);
Run Code Online (Sandbox Code Playgroud)
这里string b = true;实际上没有编译器所接受.
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
Run Code Online (Sandbox Code Playgroud)
这样做的原因是方便(连接字符串是一项常见任务).
正如BoltClock所说,'+'运算符是在数字类型,字符串上定义的,也可以为我们自己的类型定义(运算符重载).
如果参数的类型上没有重载的"+"运算符且它们不是数字类型,则编译器默认为字符串连接.
编译器String.Concat(...)在使用"+"连接时插入一个调用,并且Concat的实现在传递给它的每个对象上调用ToString.
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
Run Code Online (Sandbox Code Playgroud)
疯??不,背后必然有一个原因.
有人打电话Eric Lippert......
| 归档时间: |
|
| 查看次数: |
5323 次 |
| 最近记录: |