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

Rez*_*eza 55 .net c# .net-6.0

我有这段代码检查两个变量的引用,我遇到了这种情况,这有点令人困惑:

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

Console.WriteLine($"{first == second}   {first == third}");
Run Code Online (Sandbox Code Playgroud)

结果是:False True

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

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

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

Console.WriteLine($"{first == second}   {first == third}");
Run Code Online (Sandbox Code Playgroud)

那么结果就变成了:True True

为什么会出现这种情况?

Gur*_*ron 37

\n

我不知道为什么当你把它从 10 改为 1 时它会改变

\n
\n

我相信这是一个实现细节,您不应该依赖它(将尝试在规范中查找某些内容),但一些正的个位数数字已缓存在 .NET Core 的实现中int.ToStringUInt32ToDecStr这是内部调用的摘录int.ToString

\n
// For single-digit values that are very common, especially 0 and 1, just return cached strings.\nif (bufferLength == 1)\n{\n    return s_singleDigitStringCache[value];\n}\n
Run Code Online (Sandbox Code Playgroud)\n

至于平等 - 请检查:

\n
    \n
  1. C# == 和 Equals() 之间的区别
  2. \n
  3. .Net Framework 中的字符串实习。(编译器将实习字符串文字,因此它们都将指向内存中的相同地址)
  4. \n
  5. 使用类型动态
  6. \n
\n

更新:

\n

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

\n
// For single-digit values that are very common, especially 0 and 1, just return cached strings.\nif (bufferLength == 1)\n{\n    return s_singleDigitStringCache[value];\n}\n
Run Code Online (Sandbox Code Playgroud)\n

更新2:

\n

此 PR出于性能原因引入了缓存,并在.NET Core 3.0 博客文章中的性能改进中提到:

\n
\n

在一些大型Web应用程序中,我们发现托管堆上的大量字符串都是简单的整数值,例如\xe2\x80\x9c0\xe2\x80\x9d和\xe2\x80\x9c1\xe2\x80\x9d。由于最快的代码是您根本不需要执行的代码,为什么要一遍又一遍地分配和格式化这些小数字,而我们可以只是缓存和重用结果(实际上是我们自己的字符串实习池) ?这就是 PR dotnet/coreclr#18383 所做的,为 \xe2\x80\x9c0\xe2\x80\x9d 到 \xe2\x80\x9c9\xe2\x80\ 创建一个小型的、专门的字符串缓存。 x9d,现在每当我们发现自己正在格式化一位数字整数基元时,我们都会从该缓存中获取相关字符串。

\n
\n
var dict = new Dictionary<int, string>()\n{\n    {0, "0"},\n    {1, "1"},\n    {2, "2"},\n    {3, "3"},\n    {4, "4"},\n    {5, "5"},\n    {6, "6"},\n    {7, "7"},\n    {8, "8"},\n    {9, "9"},\n    {10, "10"},\n};\n\nforeach(var kvp in dict)\n{\n    Console.WriteLine(object.ReferenceEquals(kvp.Key.ToString(), kvp.Value));\n}\n
Run Code Online (Sandbox Code Playgroud)\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
方法工具链意思是错误标准差比率第0代第一代第2代已分配
单个数字到字符串netcoreapp2.117.72纳秒0.3273纳秒0.3061纳秒1.000.0152\xe2\x80\x93\xe2\x80\x9332乙
单个数字到字符串netcoreapp3.011.57纳秒0.1750纳秒0.1551纳秒0.65\xe2\x80\x93\xe2\x80\x93\xe2\x80\x93\xe2\x80\x93
\n

  • 哇,确实是缓存问题!从来没想过他们会出于某种原因缓存它们。 (4认同)
  • @eocron 我相信这是出于性能原因而完成的。据我所知,Java 中也做了类似的事情。 (2认同)

rfm*_*tor 9

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

firstthird都是 type string,即使只在运行时知道,因此System.String's 运算符==重写被称为 and :

...依次调用静态 Equals(String, String) 方法,该方法执行序号(区分大小写且不区分区域性)比较。

(来源)

我还要指出,Visual Studio 在以下位置提供了CS0253编译器警告first == second

可能出现意外的参考比较;要进行值比较,请将右侧转换为类型“string”

至于第二个问题...参见@GuruStron的回答