为什么连接空字符串有效,但调用“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
为什么第一个语句有效?
第一个工作的原因:
从 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.Empty
和 null
之间没有区别。您也可以将 null 传递到string.Format
中。但是您正在尝试在null
上调用方法,这总是会导致NullReferenceException
,从而生成编译器错误。
如果出于某种原因您真的想这样做,您可以编写一个扩展方法,该方法检查null
然后返回string.Empty
.但是这样的扩展应该只在绝对必要时使用(在我看来)。
一般而言:根据规范,接受 null 作为参数可能有效,也可能无效,但在 null 上调用方法始终无效。
这就是为什么 + 运算符的操作数在字符串的情况下可以为 null 的其他主题。这有点像VB的东西(对不起,伙计们),让程序员的生活更轻松,或者假设程序员不能处理空值。我完全不同意这个规范。"未知"+"任何东西"应该仍然是"未知"......