Object.ReferenceEquals为两个不同的对象输出true

时间:2016-05-06 09:27:12

标签: c#

以下代码如何打印true

string x = new string(new char[0]);
string y = new string(new char[0]);
Console.WriteLine(object.ReferenceEquals(x,y));

我希望这能打印False,因为我期望构建两个单独的对象,然后比较它们的引用。

3 个答案:

答案 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设置为"",则可以看到相同的行为。