Object.ReferenceEquals 为两个不同的对象打印 true

Kyl*_*Ren 5 c#

下面的代码是如何打印的true

string x = new string(new char[0]);
string y = new string(new char[0]);
Console.WriteLine(object.ReferenceEquals(x,y));
Run Code Online (Sandbox Code Playgroud)

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

Jon*_*eet 5

这是 CLR 中未记录的(据我所知)优化。这很奇怪,但是是的:new操作符从两个调用中返回相同的引用。

它似乎在 CoreCLR 以及 Linux(甚至在 Mono)上实现。

字符串构造函数是我见过的唯一示例,尽管如注释中所述,您可以使用其他构造函数重载来激发它。

我确信这是 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在 CLR 是开源的,我们可以找出执行此操作的位置。它似乎在object.cpp- 如果您搜索出​​现的次数,GetEmptyString您会看到它在构造长度为 0 的字符串时在各种情况下使用。

  • 因为不需要计算新的字符串,所以现在变成了常量@KyloRen (2认同)

Mat*_*son 5

发生这种情况是因为特殊情况是从空字符数组构造空字符串。字符串构造函数返回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
Run Code Online (Sandbox Code Playgroud)

来自字符串参考源(这是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"));
    }
}
Run Code Online (Sandbox Code Playgroud)

而且(这是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;
    }
Run Code Online (Sandbox Code Playgroud)

请注意以下几行:

        if (count == 0)
            return String.Empty;
Run Code Online (Sandbox Code Playgroud)

        else
            return String.Empty;
Run Code Online (Sandbox Code Playgroud)

  • 对实际优化的好发现!我很抱歉乔恩总是打败我们:) (2认同)
  • 实际上`String.Empty` [甚至没有被实习](https://blogs.msdn.microsoft.com/ericlippert/2009/09/28/string-interning-and-string-empty/),至少在某些.NET 运行时的版本。它不是一个常量而是一个只读字段,所以另一个奇怪的地方。 (2认同)