这是有效的C#代码
var bob = "abc" + null + null + null + "123"; // abc123
这不是有效的C#代码
var wtf = null.ToString(); // compiler error
为什么第一条语句是有效的?
第一个代码有效的原因:
来自MSDN:
在字符串连接操作中,C#编译器将null字符串视为空字符串,但不会转换原始null字符串的值。
有关+二元运算符的更多信息:
当一个或两个操作数是字符串类型时,二元+运算符执行字符串连接。
如果字符串连接的操作数为null,则会替换为空字符串。否则,任何非字符串参数都将通过调用继承自类型对象的虚拟ToString方法转换为其字符串表示形式。
如果ToString返回null,则会替换为空字符串。
第二个代码中出错的原因是:
null (C# Reference) - null关键字是表示空引用的字面量,它不引用任何对象。null是引用类型变量的默认值。
var wtf = ((String)null).ToString();
最近我在用Java工作,其中强制转换null是可能的,因为我已经有一段时间没有使用C#了。 - Jochemnull.ToString()
与ToString(null)
的区别。 - Svish因为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
?)。
+
运算符只是 String.Concat
的语法糖。 - Botz3000__arglist
和一个params
对象数组版本。 - WormboConcat
是否总是创建一个新的非空字符串数组,还是有其他情况发生? - supercat第一个示例将被翻译为:
var bob = String.Concat("abc123", null, null, null, "abs123");
Concat
方法检查输入并将 null 转换为空字符串。
第二个示例将被翻译为:
var wtf = ((object)null).ToString();
因此,这里将生成一个null
引用异常。
AccessViolationException
异常 :) - leppie((object)null).ToString()
=> AccessViolation
((object)1 as string).ToString()
=> NullReference
- leppie((object)null).ToString()
放在 try/catch
中时,我也会得到 NullReferenceException
。 - leppie((string)null).ToString()
,这会导致访问冲突。 - leppie你的代码的第一部分就像在 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()
是一个方法,只能由对象实例调用,而不能由任何字面值调用。
String.Empty
不等于 null
。在一些方法中(如 String.Concat
)它们只是被同样对待而已。例如,如果你有一个字符串变量设置为 null
,当你尝试在其上调用一个方法时,C# 不会用 String.Empty
来替换它。 - Botz3000null
并不像String.Empty
那样处理,只是在连接字符串时被视为如此。当你添加字符串时,C#编译器调用的是String.Concat
方法。 "abc" + null
会被翻译成 String.Concat("abc", null)
,而在内部,该方法将null
替换为String.Empty
。第二部分完全正确。 - Botz3000null
对象的行为更像空字符串。如果Microsoft这样做了,他们也必须使Nullable工作方式有所不同(以允许Nullable - 无论如何都应该这样做)和/或定义一个NullableString类型,它与String大多数情况下是可互换的,但不会将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
var bob = Plus(Plus(Plus(Plus("abc",null),null),null),"123");
。 操作符重载本质上是静态方法:http://msdn.microsoft.com/en-us/library/s53ehcz3(v=VS.71).aspx 如果不是这样,那么 var bob = null + "abc";
或尤其是 string bob = null + null;
将无效。 - Ben Mosher在这个讨论串中,有人说你不能从空无中创造字符串(我认为这是一个不错的说法)。但是是可以的 :-),下面的示例说明了这一点:
var x = null + (string)null;
var wtf = x.ToString();
代码完全正常,没有抛出异常。唯一的区别是您需要将其中一个null强制转换为字符串 - 如果删除(string)强制转换,则示例仍会编译,但会引发运行时异常:“运算符'+'在类型为''和''的操作数上不明确”。
N.B.在上面的代码示例中,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
时失败。null
添加到字符串中会被简单地忽略。在您的第二个示例中,null
不是任何对象的实例,因此它甚至没有ToString()
方法。它只是一个字面量。因为在连接字符串时,string.Empty
和null
没有区别。
你也可以将null传递给string.Format
。但是,如果您试图在null
上调用方法,则始终会导致NullReferenceException
并因此生成编译器错误。
如果出于某种原因您真的想这样做,您可以编写一个扩展方法,检查null
,然后返回string.Empty
。但是,在我看来,只有在绝对必要的情况下才应使用这样的扩展。
null.ToString()
命名为wtf
很奇怪。为什么会让你感到惊讶?因为当你没有任何调用来源时,你无法调用实例方法。 - BoltClockclass null_extension { String ToString( Object this arg ) { return ToString(arg); } }
- ctrl-alt-delor