使用 == 运算符比较两个对象
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];
}
至于平等 - 请检查:
- C# difference between == and Equals().
- String interning in .Net Framework。 (编译器会保留字符串文字,所以它们都指向内存中的相同地址)
- 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
–
–
–
–
第一个问题的答案是因为字符串相等性不是基于对象引用,因为默认情况下是引用类型。
first
和 third
都是类型 string
,即使只在运行时才知道,所以调用 System.String
的运算符 ==
覆盖和:
...in turn, calls the static Equals(String, String) method, which performs an
ordinal (case-sensitive and culture-insensitive) comparison.
我还要指出 Visual Studio 在 first == second
:
处提供了 CS0253 编译器警告
Possible unintended reference comparison; to get a value comparison,
cast the right hand side to type 'string'
至于第二个问题……见@GuruStron的。
我有这段代码可以检查两个变量的引用, 我遇到了这个有点令人困惑的案例:
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];
}
至于平等 - 请检查:
- C# difference between == and Equals().
- String interning in .Net Framework。 (编译器会保留字符串文字,所以它们都指向内存中的相同地址)
- 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 | – | – | – | – |
第一个问题的答案是因为字符串相等性不是基于对象引用,因为默认情况下是引用类型。
first
和 third
都是类型 string
,即使只在运行时才知道,所以调用 System.String
的运算符 ==
覆盖和:
...in turn, calls the static Equals(String, String) method, which performs an ordinal (case-sensitive and culture-insensitive) comparison.
我还要指出 Visual Studio 在 first == second
:
Possible unintended reference comparison; to get a value comparison, cast the right hand side to type 'string'
至于第二个问题……见@GuruStron的