这是有效的 C# 代码
var bob = "abc" + null + null + null + "123"; // abc123
这不是有效的 C# 代码
var wtf = null.ToString(); // compiler error
为什么第一个陈述有效?
第一个工作的原因:
来自MSDN:
在字符串连接操作中,C# 编译器将空字符串视为空字符串,但它不会转换原始空字符串的值。
有关+ 二元运算符的更多信息:
当一个或两个操作数都是字符串类型时,二元 + 运算符执行字符串连接。
如果字符串连接的操作数为 null,则替换为空字符串。
ToString
否则,通过调用从类型 object 继承的虚拟方法,将任何非字符串参数转换为其字符串表示形式。如果
ToString
返回null
,则替换为空字符串。
第二个错误的原因是:
null(C# 参考) - null 关键字是表示空引用的文字,它不引用任何对象。null 是引用类型变量的默认值。
因为+
C# 中的运算符在内部转换为String.Concat
,这是一个静态方法。null
而这个方法恰好像一个空字符串一样对待。如果您查看String.Concat
Reflector 中的源代码,您会看到:
// while looping through the parameters
strArray[i] = (str == null) ? Empty : str;
// then concatenate that string array
(MSDN 也提到它:http: //msdn.microsoft.com/en-us/library/k9c94ey1.aspx)
另一方面,ToString()
是一个实例方法,你不能调用它null
(应该使用什么类型null
?)。
第一个样本将被翻译成:
var bob = String.Concat("abc123", null, null, null, "abs123");
该Concat
方法检查输入并将 null 转换为空字符串
第二个样本将被翻译成:
var wtf = ((object)null).ToString();
所以null
这里会产生一个引用异常
您的代码的第一部分就像在String.Concat
,
这是添加字符串时 C# 编译器调用的内容。"abc" + null
被翻译成String.Concat("abc", null)
,
在内部,该方法替换null
为String.Empty
. 所以,这就是为什么你的第一部分代码不会抛出任何异常。就像
var bob = "abc" + string.Empty + string.Empty + string.Empty + "123"; //abc123
并且在代码的第二部分中抛出异常,因为“null”不是对象,null 关键字是表示空引用的文字,它不引用任何对象。null 是引用类型变量的默认值。
' ToString()
' 是一种可以由对象实例调用但不能由任何文字调用的方法。
在 .net 之前的 COM 框架中,任何接收到字符串的例程都必须在处理完字符串后释放它。因为在例程中传入和传出空字符串是很常见的,并且因为尝试“释放”空指针被定义为合法的无操作操作,Microsoft 决定让空字符串指针表示空字符串。
为了与 COM 兼容,.net 中的许多例程会将空对象解释为空字符串的合法表示。通过对 .net 及其语言的一些细微更改(最显着的是允许实例成员指示“不作为虚拟调用”),Microsoft 可以使null
声明类型 String 的对象表现得更像空字符串。如果微软这样做了,它也必须做出Nullable<T>
一些不同的工作(以便允许Nullable<String>
--他们无论如何都应该做的事情)和/或定义一个NullableString
几乎可以与 互换的类型String
,但不会考虑null
作为有效的空字符串。
实际上,在某些情况下 anull
将被视为合法的空字符串,而在其他情况下则不会。这不是一个非常有用的情况,但程序员应该注意的情况。一般来说,如果is ,表单的表达式stringValue.someMember
会失败,但大多数接受字符串作为参数的框架方法和运算符都会将其视为空字符串。stringValue
null
null
'+'
是中缀运算符。像任何运算符一样,它实际上是在调用一个方法。你可以想象一个非中缀版本"wow".Plus(null) == "wow"
实施者已经决定这样的事情......
class String
{
...
String Plus(ending)
{
if(ending == null) return this;
...
}
}
所以..你的例子变成
var bob = "abc".Plus(null).Plus(null).Plus(null).Plus("123"); // abc123
这与
var bob = "abc".Plus("123"); // abc123
null 在任何时候都不会成为字符串。所以null.ToString()
没有什么不同 null.VoteMyAnswer()
。;)
我猜是因为它是一个不引用任何对象的文字。ToString()
需要一个object
.
有人在这个讨论帖中说,你不能无中生有。 (我认为这是一个很好的短语)。但是是的 - 你可以:-),如以下示例所示:
var x = null + (string)null;
var wtf = x.ToString();
工作正常,根本不会抛出异常。唯一的区别是您需要将其中一个空值转换为字符串 - 如果您删除(string)转换,则该示例仍可编译,但会引发运行时异常:“运算符 '+' 在键入“<null>”和“<null>”。
注意在上面的代码示例中,x 的值并不像您预期的那样为空,在您将其中一个操作数转换为字符串后,它实际上是一个空字符串。
另一个有趣的事实是,在 C#/.NET中,如果您考虑不同的数据类型,处理方式null
并不总是相同的。例如:
int? x = 1; // string x = "1";
x = x + null + null;
Console.WriteLine((x==null) ? "<null>" : x.ToString());
关于代码片段的第一行x
:如果是一个可以为空的整数变量(即int?
)包含 value 1
,那么您将<null>
返回结果。如果它是带有 value 的字符串(如注释中所示)"1"
,那么您将"1"
返回而不是<null>
.
注意也很有趣:如果您使用var x = 1;
第一行,那么您会遇到运行时错误。为什么?因为赋值会将变量x
转换int
为不可为空的数据类型。编译器在这里不假设int?
,因此在null
添加的第二行中失败。
添加null
到字符串被简单地忽略。null
(在您的第二个示例中)不是任何对象的实例,因此它甚至没有ToString()
方法。这只是一个字面意思。
因为连接字符串string.Empty
之间没有区别。null
您也可以将 null 传递给string.Format
。但是您试图在 上调用一个方法null
,这总是会导致 aNullReferenceException
并因此产生编译器错误。
如果出于某种原因你真的想这样做,你可以编写一个扩展方法,检查null
然后返回string.Empty
. 但是只有在绝对必要时才应该使用这样的扩展名(在我看来)。
一般来说:根据规范接受 null 作为参数可能有效,也可能无效,但在 null 上调用方法总是无效的。
这就是为什么 + 运算符的操作数在字符串的情况下可以为空的其他主题。这是一种 VB 的东西(对不起,伙计们),让程序员的生活更轻松,或者假设程序员不能处理空值。我完全不同意这个规范。'unknown' + 'anything' 应该仍然是 'unknown'...