以下代码如何打印true
?
string x = new string(new char[0]);
string y = new string(new char[0]);
Console.WriteLine(object.ReferenceEquals(x,y));
我希望这能打印False
,因为我期望构建两个单独的对象,然后比较它们的引用。
答案 0 :(得分:4)
这是CLR中未记录的(据我所知)优化。这很奇怪,但是:new
运算符从两个调用返回相同的引用。
它似乎也在Linux上(甚至在Mono上)在CoreCLR中实现。
字符串构造函数是我见过的唯一一个例子,尽管如评论中所述,你可以用其他构造函数重载来激发它。
我确信它是CLR中的优化,因为IL正如您所期望的那样 - 并且将构造函数调用转换为不同的方法并不会改变事物:
using System;
public class Test
{
static void Main()
{
// Declaring as object to avoid using the == overload in string
object x = CreateString(new char[0]);
object y = CreateString(new char[0]);
object z = CreateString(new char[1]);
Console.WriteLine(x == y); // True
Console.WriteLine(x == z); // False
}
static string CreateString(char[] c)
{
return new string(c);
}
}
现在CLR是开源的,我们可以找到它的执行位置。它似乎位于object.cpp
中 - 如果您搜索GetEmptyString
的出现次数,您会看到在构造长度为0的字符串的各种情况下使用它。
答案 1 :(得分:4)
这是因为从空char数组构造空字符串的特殊情况。对于以这种方式构造的空字符串,字符串构造函数返回string.Empty
:
string x = new string(new char[0]);
string y = new string(new char[0]);
Console.WriteLine(object.ReferenceEquals(x, y)); // true
Console.WriteLine(object.ReferenceEquals(x, string.Empty)); // true
来自reference source for string(这是char*
参数的构造函数):
[System.Security.SecurityCritical] // auto-generated
private unsafe String CtorCharPtr(char *ptr)
{
if (ptr == null)
return String.Empty;
#if !FEATURE_PAL
if (ptr < (char*)64000)
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeStringPtrNotAtom"));
#endif // FEATURE_PAL
Contract.Assert(this == null, "this == null"); // this is the string constructor, we allocate it
try {
int count = wcslen(ptr);
if (count == 0)
return String.Empty;
String result = FastAllocateString(count);
fixed (char *dest = result)
wstrcpy(dest, ptr, count);
return result;
}
catch (NullReferenceException) {
throw new ArgumentOutOfRangeException("ptr", Environment.GetResourceString("ArgumentOutOfRange_PartialWCHAR"));
}
}
并且(这是char[]
参数的构造函数):
[System.Security.SecuritySafeCritical] // auto-generated
private String CtorCharArray(char [] value)
{
if (value != null && value.Length != 0) {
String result = FastAllocateString(value.Length);
unsafe {
fixed (char * dest = result, source = value) {
wstrcpy(dest, source, value.Length);
}
}
return result;
}
else
return String.Empty;
}
注意以下几行:
if (count == 0)
return String.Empty;
和
else
return String.Empty;
答案 2 :(得分:1)
这是因为object.Equals
首先检查引用相等性,然后在第一个变量(Equals
)上调用x
。
string.Equals
检查字符串的实际值(使用当前文化设置,这可能会影响比较),而不仅仅是引用,因此返回true
,因为两个对象具有相同的值
对于你的编辑:似乎CLRr做了一些魔术并试图评估你的new string(char[0])
,所以它可以被实习。如果将x
设置为""
,则可以看到相同的行为。