可能重复:
C#:为什么在字符串中添加 null 是合法的?
这是有效的 C# 代码:
var samsung= "xyz" + null + null + null + null + "890"; // xyz890
这是无效的 C# 代码:
var iphone= null.ToString(); // compiler error
为什么以及如何第一个语句有效而第二个语句无效?
可能重复:
C#:为什么在字符串中添加 null 是合法的?
这是有效的 C# 代码:
var samsung= "xyz" + null + null + null + null + "890"; // xyz890
这是无效的 C# 代码:
var iphone= null.ToString(); // compiler error
为什么以及如何第一个语句有效而第二个语句无效?
+
运算符用于连接字符串,因为它是字符串类的null
一个实例,所以它可以用作运算符的参数。"xyz" + null
最终返回字符串“xyz”,所以这个过程只是重复,直到你真正添加"890"
.
虽然 null 可以用作方法参数的字符串对象,但您实际上不能在其上调用方法,因为没有任何东西可以处理方法调用。
将方法视为对象处理外部请求的一种方式,事情就更有意义了。您可以向对象发送请求,并null
作为处理内容的参数,但您实际上不能要求null
处理某些内容。
C# 编译器将连接中的 null 视为空字符串。
var samsung= "xyz" + null + null + null + null + "890";
等于
var samsung= "xyz" + ""+ ""+ ""+ ""+ "890";
当您尝试调用时,null.toString()
您实际上是在尝试调用空对象上的方法,该方法(如果已编译)总是会导致NullReferenceException
.
使用第一行,您正在连接字符串。 null
什么都不是,所以基本上你得到xyz890
. 这就像做1+0+0+0+4
但是用你的第二个,你打电话ToString()
给null
. null
什么都不是,所以没有什么可以调用ToString()
的。您正在尝试在未初始化的变量上调用方法。
由于string
是引用类型,因此 null 可以隐式转换为string
. 在第一种情况下,运算符序列+
只是转换为String.Concat(obj1, obj2, obj3, ...)
要编译您的代码,您可以调用Convert.ToString
:
var iphone = Convert.ToString(null);
您可以将+
运算符视为全局静态函数,如下所示:
+(string leftHandSide, string rightHandSide) {
if (leftHandSide == null)
leftHandSide = string.Empty;
if (rightHandSide == null)
rightHandSide = string.Empty;
string returnValue;
// concatenate string
return returnValue;
}
由于这个字符串连接方法是静态的,它不需要实例来调用它,它处理空值。但是,当您尝试调用ToString
时null
,没有对象的实例,因此没有ToString
方法可以调用,因此出现异常。
这是设计使然。您可以阅读C# Language Specification。引用:
字符串连接:
string operator +(string x, string y); string operator +(string x, object y); string operator +(object x, string y);
二元运算符的这些重载
+
执行字符串连接。如果字符串连接的操作数是null
,则替换为空字符串。否则,任何非字符串参数都会通过调用ToString
从 type 继承的虚拟方法转换为其字符串表示形式object
。如果ToString
返回null
,则替换为空字符串。[...]
因此,正如其他人所说,将其+
视为一种接受两个参数并且可以接受一个或两个参数的方法null
。
因为在任何情况下都不能在引用上调用方法 在您的第一种情况下,编译器会将字符串常量预编译null
,所以编译器不允许这样做。"xyz890"
为合法赋值。
通常,+
用于string
编译的运算符String.Concat(s1, s2, ...)
支持null
参数。
在第二种情况下更准确地说,实际问题不在于它是一个空指针,而在于没有将方法绑定到的类型。正如其他评论中指出的那样,您可以null
转换为任何引用类型,并且编译得很好:
((string)null).ToString(); // compiles, but fails at run-time.
+ 运算符将被转换为一个看起来很像的静态函数:
public static string Concat(string first, string second) {...}
将 null 作为参数传递给函数不是问题。它通过就好了。该函数碰巧在连接参数之前检查两个参数是否为空;如果任何参数为空,则将它们视为空字符串,以免导致异常。这里的关键点是字符串的实例成员在此函数第一次检查为空之前不会被调用。
至于在编译时null.ToString()
对任何计算null
结果调用实例函数会导致异常。这就是这种情况的定义。
如果您查看 IL,您会发现编译器足够聪明,只需删除空值并创建连接字符串。如前所述,您不能执行 null.ToString(); 您可以执行 string.Empty 或将字符串分配为空。
.method private hidebysig static void Main() cil managed
{
.entrypoint
// Code size 8 (0x8)
.maxstack 1
.locals init ([0] string samsung)
IL_0000: nop
IL_0001: ldstr "xyz890" //null has been removed by compiler.
IL_0006: stloc.0
IL_0007: ret
} // end of method Program::Main
编辑
扩大尼尔森的评论。由于 null 表示 nil 引用,因此可以公平地说处理字符串 concat 的方式是 C# 允许的操作。我想它使用字节数组构建,它会省略空值,因此只是让它看起来好像它正在连接空值。