使用 == 运算符比较两个对象

Comparing two objects with == operator

我有这段代码可以检查两个变量的引用, 我遇到了这个有点令人困惑的案例:

string first = "10";
object second = 10.ToString();
dynamic third = second;

Console.WriteLine($"{first == second}   {first == third}");

结果是:False True

我的第一个问题是为什么第一个和第三个引用相等?如果第三个变量等于第二个,它应该是 False 因为它们的对象引用不相等。

当我将值更改为 "1" 时,我感到困惑,如下所示:

string first = "1";
object second = 1.ToString();
dynamic third = second;

Console.WriteLine($"{first == second}   {first == third}");

则结果变为:True True

为什么会这样?

I am not sure why it changes when you change it from 10 to 1

我相信这是一个实现细节,你不应该依赖它(会尝试在规范中找到一些东西)但是一些正的个位数 are cached in int.ToString implementation for .NET Core. Here is excerpt from UInt32ToDecStr which is called internally by int.ToString:

// For single-digit values that are very common, especially 0 and 1, just return cached strings.
if (bufferLength == 1)
{
    return s_singleDigitStringCache[value];
}

至于平等 - 请检查:

  1. C# difference between == and Equals().
  2. String interning in .Net Framework。 (编译器会保留字符串文字,所以它们都指向内存中的相同地址)
  3. Using type dynamic

UPD:

无法在规范中找到任何内容,但下一个代码在 .NET Framework and .NET 6 中的行为有所不同(前一个打印 11 次 False,后者打印 10 次 True 和一个 False):

var dict = new Dictionary<int, string>()
{
    {0, "0"},
    {1, "1"},
    {2, "2"},
    {3, "3"},
    {4, "4"},
    {5, "5"},
    {6, "6"},
    {7, "7"},
    {8, "8"},
    {9, "9"},
    {10, "10"},
};

foreach(var kvp in dict)
{
    Console.WriteLine(object.ReferenceEquals(kvp.Key.ToString(), kvp.Value));
}

UPD2:

this PR and is mentioned in Performance Improvements in .NET Core 3.0 博文出于性能原因引入了缓存:

In some sizeable web applications, we found that a large number of strings on the managed heap were simple integral values like “0” and “1”. And since the fastest code is code you don’t need to execute at all, why bother allocating and formatting these small numbers over and over when we can instead just cache and reuse the results (effectively our own string interning pool)? That’s what PR dotnet/coreclr#18383 does, creating a small, specialized cache of the strings for “0” through “9”, and any time we now find ourselves formatting a single-digit integer primitive, we instead just grab the relevant string from this cache.

private int _digit = 4;

[Benchmark]
public string SingleDigitToString() => _digit.ToString();
Method Toolchain Mean Error StdDev Ratio Gen 0 Gen 1 Gen 2 Allocated
SingleDigitToString netcoreapp2.1 17.72 ns 0.3273 ns 0.3061 ns 1.00 0.0152 32 B
SingleDigitToString netcoreapp3.0 11.57 ns 0.1750 ns 0.1551 ns 0.65

第一个问题的答案是因为字符串相等性不是基于对象引用,因为默认情况下是引用类型。

firstthird 都是类型 string,即使只在运行时才知道,所以调用 System.String 的运算符 == 覆盖和:

...in turn, calls the static Equals(String, String) method, which performs an ordinal (case-sensitive and culture-insensitive) comparison.

(source)

我还要指出 Visual Studio 在 first == second:

处提供了 CS0253 编译器警告

Possible unintended reference comparison; to get a value comparison, cast the right hand side to type 'string'

至于第二个问题……见@GuruStron的