提问人:Skary 提问时间:11/24/2021 最后编辑:Uwe KeimSkary 更新时间:11/24/2021 访问量:320
装箱类型相等键和字典键
Boxing type equality and dictionary keys
问:
当涉及到盒装类型时,我对字典如何比较键有点困惑。
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
int i = 5;
int n = 5;
object boxedI = i;
object boxedN = n;
Console.WriteLine("i == n ? " + (i == n) ); //true
Console.WriteLine("bI == bN ? " + (boxedI == boxedN) ); //false
Dictionary<object,int> _dict = new Dictionary<object,int> ();
_dict.Add(boxedI,5);
Console.WriteLine("_dict contains boxedI? " + _dict.ContainsKey(boxedI) ); //true
Console.WriteLine("_dict contains boxedN? " + _dict.ContainsKey(boxedN) ); //!! also true, surprise me
_dict.Add(boxedN,5);//exception
}
}
我预计由于相等运算符“失败”(AFAIK,它基于方法GetHashCode,与字典用于构建其内部哈希表表单对象的方法相同),那么字典也应该“失败”盒装I和N的比较,但事实并非如此。
这是我使用的小提琴:https://dotnetfiddle.net/DW54nN
所以我问是否有人可以向我解释这里附加了什么以及我的心智模型中缺少什么。
答:
这是一个引用与值类型的事情:
int i = 5;
int n = 5;
这些是值类型并被放在堆栈上,因此当我们比较它们时,我们转到堆栈,可以说 i 和 n 的值是 5,这使它们“相等”。
object boxedI = i;
object boxedN = n;
当你把这些值放进去时,你会创建一个“引用”类型,这意味着一个值被放入堆中,一个引用被放在堆栈上,所以你可以想象在堆栈上有:object
#0005 -> boxedI
#0006 -> boxedN
现在,当您执行相等操作时,您正在比较哪些不相同#0005 == #0006
但是,当您传入或传入该方法时,该方法知道如何遵循对堆 (5) 上值的引用(或指针)。boxedI
boxedN
ContainsKey
因此,当你要求你正在做的事情时,就是要求(粗略地说)ContainsKey(boxedI)
ContainsKey(5)
这就是为什么这两者是“平等的”
评论
==
object
Equals
TLDR:比较装箱值 using 使用装箱对象的引用相等,但使用 比较盒装值使用基础值的 .==
Equals()
Equals()
装箱值类型时,装箱对象和方法实现将调用装箱值的版本。GetHashCode()
Equals()
也就是说,给定:
- 正确实现的值类型。
VT
GetHashCode()
Equals()
- 的实例。
x
VT
- 的值与 相同的实例。
y
VT
x
- 的盒装实例 : 。
x
bx
- 的盒装实例 : 。
y
by
情况如下:
x.Equals(y) == true // Original values are equal
bx.Equals(by) == true // Boxed values are equal
x.GetHashCode() == y.GetHashCode() // Original hashes are equal
bx.GetHashCode() == by.GetHashCode() // Boxed hashes are equal
bx.GetHashCode() == x.GetHashCode() // Original hash code == boxed hash code
但是,==
运算符不是由盒装版本委托的,实际上它是使用引用相等来实现的,因此:
(x == y) == true // Original values are equal using "=="
(bx == by) == false // Boxed values are not equal using "=="
ReferenceEquals(bx, by) == false // References differ
Dictionary 使用 和 用于比较对象,并且由于它们委托给基础值,因此它可以正常工作。GetHashCode()
Equals()
以下程序演示了这一点:
using System;
namespace Demo
{
struct MyStruct: IEquatable<MyStruct>
{
public int X;
public bool Equals(MyStruct other)
{
return X == other.X;
}
public override bool Equals(object obj)
{
if (obj is not MyStruct other)
return false;
return X == other.X;
}
public override int GetHashCode()
{
return -X;
}
}
class Program
{
static void Main()
{
var x = new MyStruct { X = 42 };
var y = new MyStruct { X = 42 };
object bx = x;
object by = y;
Console.WriteLine(bx.GetHashCode()); // -42
Console.WriteLine(y.GetHashCode()); // -42
Console.WriteLine(bx.Equals(by)); // True
Console.WriteLine(bx == by); // False
Console.WriteLine(object.ReferenceEquals(bx, by)); // False
}
}
}
评论
_dict.ContainsKey(boxedN)
boxedI.Equals(boxedN)