[英]Object.ReferenceEquals prints true for two different objects
How is the below code is printing true
? 以下代码如何打印
true
?
string x = new string(new char[0]);
string y = new string(new char[0]);
Console.WriteLine(object.ReferenceEquals(x,y));
I expected this to print False
, because I expected two separate objects to be constructed and then their references compared. 我希望这打印
False
,因为我期望构建两个单独的对象,然后比较它们的引用。
This is an undocumented (as far as I'm aware) optimization in the CLR. 这是CLR中未记录的(据我所知)优化。 It's very odd, but yes: the
new
operator is returning the same reference from two calls. 这很奇怪,但是:
new
运算符从两个调用返回相同的引用。
It appears to be implemented in CoreCLR as well on Linux (and even on Mono). 它似乎也在Linux上(甚至在Mono上)在CoreCLR中实现。
The string constructor is the only example of this that I've seen, although as noted in comments you can provoke it with other constructor overloads. 字符串构造函数是我见过的唯一一个例子,尽管如评论中所述,你可以用其他构造函数重载来激发它。
I'm convinced it's an optimization in the CLR, as the IL is as you'd expect it - and moving the constructor call into a different method doesn't change things either: 我确信它是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);
}
}
Now that the CLR is open source, we can find out where this is performed. 现在CLR是开源的,我们可以找到它的执行位置。 It appears to be in
object.cpp
- if you search for occurrences of GetEmptyString
you'll see it used in various cases when a string of length 0 is being constructed. 它似乎在
object.cpp
- 如果你搜索GetEmptyString
出现,你会看到它在构造长度为0的字符串的各种情况下使用。
This happens because special case is made for constructing empty strings from empty char arrays. 发生这种情况是因为从空char数组构造空字符串的特殊情况。 The string constructor returns
string.Empty
for empty strings constructed in this way: 字符串构造函数返回以这种方式构造的空字符串的
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
From the reference source for string (this is the constructor for a char*
parameter): 从字符串的引用源 (这是
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"));
}
}
And also (this is the constructor for a char[]
parameter): 而且(这是
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;
}
Note the lines: 注意这些行:
if (count == 0)
return String.Empty;
and 和
else
return String.Empty;
That is because object.Equals
first checks on reference equality, and then calls Equals
on the first variable ( x
). 这是因为
object.Equals
首先检查引用相等性,然后在第一个变量( x
)上调用Equals
。
string.Equals
checks against the actual value of a string (using the current culture settings, which may influence the comparison), not only the reference, so it returns true
since both objects have the same value. string.Equals
检查字符串的实际值(使用当前文化设置,这可能会影响比较),而不仅仅是引用,因此它返回true
因为两个对象具有相同的值。
For your edit: it seems that the CLRr does some magic and tries to evaluate your new string(char[0])
, so it can be interned. 对于你的编辑:似乎CLRr做了一些魔法并试图评估你的
new string(char[0])
,所以它可以被实习。 You can see the same behavior if you set x
to ""
. 如果将
x
设置为""
则可以看到相同的行为。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.