为什么添加两个空string的结果不为空?
当我在.Net编程中遇到这种奇怪的行为时,我正在C#中摆弄。
我写了这个代码:
static void Main(string[] args) { string xyz = null; xyz += xyz; TestNullFunc(xyz); Console.WriteLine(xyz); Console.Read(); } static void TestNullFunc(string abc) { if (abc == null) { Console.WriteLine("meow THERE ! "); } else { Console.WriteLine("No Meow "); } }
我得到的输出为No meow
,这意味着string不为null
。 这怎么可能? 为什么添加两个null
string会导致非null
string?
在debugging时,我检查xyz
的值后添加到自己,它的值是""
(无字符)。
来自MSDN :
在string连接操作中,C#编译器将空string视为空string,
即使xyz
为null,调用+ =运算符(将其转换为对其+运算符 (*)的调用)不会抛出NullReferenceException
因为它是一个静态方法。 在伪代码中:
xyz = String.+(null, null);
然后这个实现将会像解释一样
xyz = String.+("", "");
(*)C#规范的第7.17.2节:
x op = yforms的操作是通过应用二元运算符重载分辨率(§7.3.4)来处理的,就好像操作被写入x op y一样 。
当你使用+=
运算符时,实际上是调用了string.Concat方法,就像文档中所述:
该方法连接str0和str1; 它不添加任何分隔符。 空string用于代替任何空参数。
其实这个代码:
string xyz = null; xyz += xyz;
将编入:
IL_0000: ldnull IL_0001: stloc.0 // xyz IL_0002: ldloc.0 // xyz IL_0003: ldloc.0 // xyz IL_0004: call System.String.Concat IL_0009: stloc.0 // xyz
如上所述,为什么连接null的原因与连接空string相同。
值得考虑为什么这种行为是有用的。
通常,当一个操作数为空时,我们可以用二元运算符做两件明智的事情:
- 结果是空的。
- 这个操作是没有操作的,我们剩下另一个操作数。
((int?)null) + 3
结果为null
是有意义的,一般来说这可能是最有用的结果,或者我们会自觉防范的(也就是说,我们将添加代码来捕获空的情况下明确)。
但是有两个原因不能用string连接来完成。
首先要考虑的是,由于连接意味着不是一个算术计算,而是把两件事情粘合在一起,那么什么是在事物的开始或结束时使用null的最合理的结果? 很容易使这个应该什么也不做,而不是返回null。
第二个是在实践中,我们希望带有stringa + b + c + d
如果其中任何一个为空,就会返回null的情况比那些我们不会的情况less。
从这个angular度来看,将null作为串联中的空string是有意义的。 从这个基础上来说, (string)null + (string)null
结果是""
,因为我们没有特别的情况来连接太空。
可以添加特殊情况,但是x + "" == x + null
将不再成立,这可能会导致一些奇怪的情况。
那是因为运算符+=
将空值添加到空string。
因此,编译器将添加空string添加到现有的string对象。
所以它是空的而不是空的。
尝试这个…
static void TestNullFunc(string abc) { if (string.IsNullOrEmpty( abc)) { Console.WriteLine("meow THERE ! "); } else { Console.WriteLine("No Meow "); } }
C#借用Java的+
运算符的行为。 如果两个操作数之一是一个string,那么+
操作符将调用String.Concat
,它接受Object
types,并在传递给它的每个非null对象上连接调用ToString
的结果。 空引用简单地被忽略的事实只是String.Concat
操作数本身不被视为“string”的一小部分; 这种行为更为引人注目的一点是,不是string的types的ToString
方法被调用,不pipe它们是否会被隐式地转换为string
。