为什么连接空字符串有效,但调用“null”无效.ToString()”

本文关键字:null 无效 ToString 调用 连接 字符串 有效 为什么 | 更新日期: 2023-09-27 18:35:56

这是有效的 C# 代码

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

这不是有效的 C# 代码

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

为什么第一个语句有效?

为什么连接空字符串有效,但调用“null”无效.ToString()”

第一个工作的原因:

从 MSDN:

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

有关 + 二进制运算符的详细信息:

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

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

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

秒内出错的原因是:

null(C# 参考) - null 关键字是表示 null 引用的文本,该引用不引用任何对象。 null 是引用类型变量的默认值。

因为 C# 中的 + 运算符在内部转换为 String.Concat ,这是一个静态方法。而这种方法恰好将null视为空字符串。如果你在Reflector中查看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应该使用什么类型?

第一个样本将转换为:

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 引用的文本,该引用不引用任何对象。 null 是引用类型变量的默认值。

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

在.net之前的COM框架中,任何收到字符串的例程都必须在完成时释放它。 由于空字符串传入和传出例程是很常见的,并且尝试"释放"空指针被定义为合法的无操作操作,因此Microsoft决定让空字符串指针表示空字符串。

为了与 COM 保持某种兼容性,.net 中的许多例程会将 null 对象解释为空字符串的法律表示形式。 通过一些细微的更改.net及其语言(最值得注意的是允许实例成员指示"不要作为虚拟调用"),Microsoft可以使声明类型String null对象的行为更像空字符串。 如果Microsoft这样做了,它还必须使Nullable<T>的工作方式有所不同(以便允许Nullable<String> - 恕我直言,他们应该这样做)和/或定义一个NullableString类型,该类型大多可以与String互换,但不会将null视为有效的空字符串。

实际上,在某些情况下,null将被视为合法的空字符串,而在其他上下文中则不会。 这不是一个非常有用的情况,但程序员应该意识到这种情况。 一般来说,如果null stringValue,形式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 都不会成为字符串。所以null.ToString()null.VoteMyAnswer()没有什么不同.;)

我猜是因为它是一个引用任何对象的文字。 ToString()需要一个object.

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

有人在这个讨论线程中说,你不能无中生有地制作一个字符串。(我认为这是一个很好的短语)。但是是的 - 你可以 :-),如以下示例所示:

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

工作正常,根本不会引发异常。唯一的区别是您需要将其中一个 null 转换为字符串 - 如果删除 (string) 强制转换,则示例仍可编译,但引发运行时异常:"运算符 '+' 在类型为 '' 和 '' 的操作数上不明确"。

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

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

<小时 />

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

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

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

:注:另外有趣的是:如果您将var x = 1;用于第一行,则会收到运行时错误。为什么?因为赋值会将变量x转换为数据类型 int ,这是不可空的。编译器不在此处假定int?,因此在添加null的第二行失败。

因为当您

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

一般而言:根据规范,接受 null 作为参数可能有效,也可能无效,但在 null 上调用方法始终无效。


这就是为什么 + 运算符的操作数在字符串的情况下可以为 null 的其他主题。这有点像VB的东西(对不起,伙计们),让程序员的生活更轻松,或者假设程序员不能处理空值。我完全不同意这个规范。"未知"+"任何东西"应该仍然是"未知"......