为什么连接 Null 字符串而不调用“ Null.ToString()”是有效的?

这是有效的 C # 代码

var bob = "abc" + null + null + null + "123";  // abc123

这不是有效的 C # 代码

var wtf = null.ToString(); // compiler error

为什么第一条陈述有效?

20849 次浏览

因为 C # 中的 +操作符在内部转换为 String.Concat,这是一个静态方法。这个方法恰好把 null当作一个空字符串。如果你查看反射器中 String.Concat的源代码,你会看到:

// 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应该使用什么类型?).

我猜是因为它是一个不引用任何对象的 字面意思ToString()需要一个 object

第一个起作用的原因:

来自 MSDN:

在字符串串联操作中,C # 编译器将空字符串视为空字符串,但不转换原始空字符串的值。

关于 + 二进制运算符的更多信息:

当一个或两个操作数都是字符串类型时,二进制 + 运算符执行字符串串联。

如果字符串串联的操作数为空,则替换为空字符串。否则,任何非字符串参数都将通过调用从类型对象继承的虚拟 ToString方法转换为其字符串表示形式。

如果 ToString返回 null,则替换为空字符串。

第二个错误的原因是:

Null (C # Reference) -null 关键字是一个表示 null 引用的文本,它不引用任何对象。Null 是引用类型变量的默认值。

第一个样本将被翻译成:

var bob = String.Concat("abc123", null, null, null, "abs123");

Concat方法检查输入并将 null 转换为空字符串

第二个样本将被翻译成:

var wtf = ((object)null).ToString();

因此,这里将生成一个 null引用异常

null添加到字符串只是被忽略。null(在第二个示例中)不是任何对象的实例,因此它甚至没有 ToString()方法。只是字面意思。

因为在连接字符串时,string.Emptynull之间没有区别。 您也可以将 null 传递给 string.Format。但是您试图在 null上调用一个方法,这将总是导致 NullReferenceException,因此会生成一个编译器错误。
如果出于某种原因,您真的想这样做,您可以编写一个扩展方法,检查 null,然后返回 string.Empty。但是像这样的扩展应该只在绝对必要的时候使用(在我看来)。

代码的第一部分在 String.Concat中也是这样处理的,

这是 C # 编译器在添加字符串时调用的,

在内部,该方法将 null替换为 String.Empty。所以,这就是为什么代码的第一部分不抛出任何异常。就像

var bob = "abc" + string.Empty + string.Empty + string.Empty + "123";  //abc123

在代码的第2部分,抛出异常,因为“ null”不是一个对象 Null 关键字是表示 null 引用的文本,它不引用任何对象。Null 是引用类型变量的默认值。

ToString()”是一个可以被对象实例调用的方法,但不能被任何文字调用。

在之前的 COM 框架中。Net 中,对于任何接收字符串的例程来说,在完成后释放字符串都是必要的。因为空字符串在例程中的传入和传出是非常普遍的,并且因为尝试“释放”空指针被定义为一个合法的什么都不做的操作,微软决定让一个空字符串指针代表一个空字符串。

为了允许与 COM 具有某种兼容性,在。Net 将空对象解释为法律表示形式的空字符串。只需要做一些小小的改变。Net 和它的语言(最显著的是允许实例成员指示“不作为虚拟调用”) ,微软本可以使声明类型为 String 的 null对象的行为更像空字符串。如果微软这样做了,它也必须使 Nullable<T>的工作有所不同(以便允许 Nullable<String>——恕我直言,他们无论如何都应该这样做)和/或定义一个 NullableString类型,这将是大部分与 String可互换,但不会认为一个 null是一个有效的空字符串。

实际上,在某些上下文中,null将被视为合法的空字符串,而在其他上下文中,它不会被视为合法的空字符串。这不是一个非常有帮助的情况,但是程序员应该意识到这一点。一般来说,如果 stringValuenull,那么表单 stringValue.someMember的表达式就会失败,但是大多数接受字符串作为参数的框架方法和运算符将 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.ToString()null.VoteMyAnswer()没有什么不同

有人在这个讨论帖子里说,你不能无中生有地创造一个字符串。 但是 是的,你可以: ——) ,如下面的例子所示:

var x = null + (string)null;
var wtf = x.ToString();

工作正常,根本不引发异常。唯一的区别是,您需要将一个 null 强制转换为字符串-如果您删除了 (字符串)强制转换,那么示例仍然会编译,但会抛出一个运行时异常: “操作符’+’在类型为’< null >’和’< null >’”的操作数上是不明确的。

在上面的代码示例中,x的值并不像您预期的那样为 null,实际上在您将一个操作数强制转换为字符串之后,它是一个空字符串。

略有不同的是 var a = ((string)null).ToString();-它编译但会抛出 NullReferenceException。在这种情况下,抛出异常是因为不允许对空值使用 .运算符。在这里使用 ?.是可行的(但是在本例中不执行 ToString)。编译器将正确地“创建”变量 a作为字符串。


另一个有趣的事实是,在 C #/. NET 对待 null的方式并不总是相同的中,如果考虑到不同的数据类型,例如:

int? x = 1;  //  string x = "1";
x = x + null + null;
Console.WriteLine((x==null) ? "<null>" : x.ToString());

注意代码片段的 第一排: 如果 x是一个包含值 1的可为空的整数变量(即 int?) ,那么您将得到返回的结果 <null>。如果它是一个值为 "1"的字符串(如注释中所示) ,则返回的是 "1"而不是 <null>

另外有趣的是: 如果第一行使用的是 var x = 1;,那么会得到一个运行时错误。为什么?因为赋值将把变量 x转换为数据类型 int,而 int不能为空。编译器在这里不假设 int?,因此在添加 null的第2行中失败。

一般来说: 接受 null 作为参数可能有效,也可能无效,这取决于规范,但对 null 调用方法总是无效的。


这就是为什么 + 运算符的操作数在字符串情况下可以为空的原因。这是一种 VB 的东西(对不起,伙计们) ,使程序员的生活更容易,或假设程序员不能处理空值。我完全不同意这个说明。“未知”+ “任何东西”应该还是“未知”..。