当我在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
,这意味着字符串不是null
。这怎么可能?为什么添加两个null
字符串会导致非null
字符串?
在调试时,当我将xyz
的值添加到它自己之后检查它的值时,它的值是""
(没有字符)。
来自MSDN:
在字符串串联操作中,C#编译器将空字符串视为空字符串,
即使xyz
为null,对其调用+=运算符(转换为对+运算符(*)的调用)也不会抛出NullReferenceException
,因为它是一个静态方法。在伪代码中:
xyz = String.+(null, null);
然后,实现将把它解释为
xyz = String.+("", "");
(*)C#规范第7.17.2节:
形式为x op=y的操作通过应用二进制运算符重载解析(§7.3.4)进行处理,就好像该操作是写xopy。
使用+=
运算符时,实际上是在调用字符串。Concat方法,如文档中所述:
该方法连接str0和str1;它不添加任何分隔符。使用空字符串来代替任何null参数
事实上,这个代码:
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与连接空字符串相同。
值得考虑的是,为什么这种行为是有用的。
通常,当其中一个操作数为空时,我们可以对二进制运算符做两件明智的事情:
- 结果为null
- 该操作是一个非操作数,剩下另一个操作数
例如,((int?)null) + 3
导致null
是有道理的,通常这要么是最有用的结果,要么是我们有意识地防范的结果(也就是说,我们将添加代码来明确捕获null大小写)。
但是,有两个原因不使用字符串串联。
首先要考虑的是,既然串联不是算术计算,而是将两件事粘在一起,那么在某件事的开头或结尾加零最合理的结果是什么?很容易证明这应该什么都不做,而不是返回null。
第二个是,在实践中,如果其中任何一个字符串为null,那么我们希望带字符串的a + b + c + d
返回null的情况会比不希望返回null的更少。
因此,将null视为串联中的空字符串是有意义的。在此基础上,(string)null + (string)null
导致""
是因为我们没有连接过多null的特殊情况。
可以添加该特殊情况,但x + "" == x + null
的属性将不再具有,这可能会导致一些奇怪的情况。
这是因为运算符+=
将Null添加到Empty字符串。
因此,编译器会向现有字符串对象添加空字符串。
所以它是空的,不是空的。
试试这个。。。
static void TestNullFunc(string abc)
{
if (string.IsNullOrEmpty( abc))
{
Console.WriteLine("meow THERE ! ");
}
else
{
Console.WriteLine("No Meow ");
}
}
C#从Java借用了其+
运算符的行为。如果+
的任何一个操作数是字符串,+
运算符将调用String.Concat
,后者接受类型Object
,并在传递给它的每个非null对象上连接调用ToString
的结果。忽略null引用只是String.Concat
的操作数本身不被视为"字符串"的一小部分;这种行为的一个更值得注意的方面是,非字符串类型的ToString
方法被调用,无论它们是否可以隐式转换为string
。