C# 是否应为IEquatable实现GetHashCode<;T>;关于可变类型?
我正在实现C# 是否应为IEquatable实现GetHashCode<;T>;关于可变类型?,c#,immutability,mutable,gethashcode,iequatable,C#,Immutability,Mutable,Gethashcode,Iequatable,我正在实现IEquatable,在可变类上的GetHashCode覆盖上,我很难找到一致意见 以下资源都提供了一个实现,其中GetHashCode将在对象的生命周期内返回不同的值(如果对象发生更改): 但是,声明不应该为可变类型实现GetHashCode,因为如果对象是集合的一部分,它可能会导致不良行为(这也是我的理解) 有趣的是,实现的GetHashCode仅使用不可变属性,这符合我的理解。但我不明白为什么其他资源没有涵盖这一点。他们完全错了吗 如果一个类型根本没有不可变的属性,那么
IEquatable
,在可变类上的GetHashCode
覆盖上,我很难找到一致意见
以下资源都提供了一个实现,其中GetHashCode
将在对象的生命周期内返回不同的值(如果对象发生更改):
GetHashCode
,因为如果对象是集合的一部分,它可能会导致不良行为(这也是我的理解)
有趣的是,实现的GetHashCode
仅使用不可变属性,这符合我的理解。但我不明白为什么其他资源没有涵盖这一点。他们完全错了吗
如果一个类型根本没有不可变的属性,那么当我重写Equals(object)
时,编译器会警告说GetHashCode
丢失。在这种情况下,我应该实现它并只调用base.GetHashCode()
还是只禁用编译器警告,或者我是否遗漏了一些内容,并且GetHashCode
应该始终被覆盖和实现?事实上,如果建议不应该为可变类型实现GetHashCode
,为什么还要为不可变类型实现呢?与默认的GetHashCode
实现相比,它仅仅是为了减少冲突,还是实际上添加了更具体的功能
总结一下我的问题,我的难题是在可变对象上使用GetHashCode
,这意味着如果对象上的属性发生变化,它可以在对象的生命周期内返回不同的值。但不使用它意味着,比较可能等价的对象的好处将失去,因为它将始终返回唯一的值,因此集合将始终返回使用Equals
进行操作
打印完这个问题后,在“类似问题”框中弹出,似乎是针对同一主题。答案似乎很明确,在
GetHashCode
实现中只应使用不可变属性。如果没有,那就不要写了<代码>字典仍能正常工作,尽管性能不是O(1)。这取决于您所说的集合类型。对于我的回答,我假设您谈论的是基于的哈希表集合,特别是.NET字典和键计算
因此,确定如果修改键
(假设键
是一个进行自定义哈希代码
计算的类)将发生什么的最佳方法是查看.NET源代码。从.NET源代码中,我们可以看到您的键值对
现在被包装到条目
结构中,该结构携带哈希代码
,该哈希代码是根据您的值的加法
计算的。这意味着,如果在添加密钥后更改HashCode
值,它将无法在字典中找到值
证明它的代码:
static void Main()
{
var myKey = new MyKey { MyBusinessKey = "Ohai" };
var dic = new Dictionary<MyKey, int>();
dic.Add(myKey, 1);
Console.WriteLine(dic[myKey]);
myKey.MyBusinessKey = "Changing value";
Console.WriteLine(dic[myKey]); // Key Not Found Exception.
}
public class MyKey
{
public string MyBusinessKey { get; set; }
public override int GetHashCode()
{
return MyBusinessKey.GetHashCode();
}
}
static void Main()
{
var myKey=new myKey{MyBusinessKey=“Ohai”};
var dic=新字典();
dic.Add(myKey,1);
控制台写入线(dic[myKey]);
myKey.MyBusinessKey=“更改值”;
Console.WriteLine(dic[myKey]);//找不到键异常。
}
公共类MyKey
{
公共字符串MyBusinessKey{get;set;}
公共覆盖int GetHashCode()
{
返回MyBusinessKey.GetHashCode();
}
}
来回答你的问题。您希望有不可变的值,您的hashcode
计算基于这些值
另一点,如果不重写GetHashCode
,则自定义类的hashcode
将基于对象的引用。因此,对于基础值相同的不同对象返回相同的hashcode
的担忧可以通过重写GetHashCode
方法并根据业务键计算hashcode
来缓解。例如,您有两个字符串属性,要计算hashcode,您需要concat
strings
并调用basestring
GetHashCode
方法。这将保证对于对象的相同基础值,您将获得相同的hashcode
,这取决于您所谈论的集合类型。对于我的回答,我假设您谈论的是基于的哈希表集合,特别是.NET字典和键计算
因此,确定如果修改键
(假设键
是一个进行自定义哈希代码
计算的类)将发生什么的最佳方法是查看.NET源代码。从.NET源代码中,我们可以看到您的键值对
现在被包装到条目
结构中,该结构携带哈希代码
,该哈希代码是根据您的值的加法
计算的。这意味着,如果在添加密钥后更改HashCode
值,它将无法在字典中找到值
证明它的代码:
static void Main()
{
var myKey = new MyKey { MyBusinessKey = "Ohai" };
var dic = new Dictionary<MyKey, int>();
dic.Add(myKey, 1);
Console.WriteLine(dic[myKey]);
myKey.MyBusinessKey = "Changing value";
Console.WriteLine(dic[myKey]); // Key Not Found Exception.
}
public class MyKey
{
public string MyBusinessKey { get; set; }
public override int GetHashCode()
{
return MyBusinessKey.GetHashCode();
}
}
static void Main()
{
var myKey=new myKey{MyBusinessKey=“Ohai”};
var dic=新字典();
dic.Add(myKey,1);
控制台写入线(dic[myKey]);
myKey.MyBusinessKey=“更改值”;
Console.WriteLine(dic[myKey]);//找不到键异常。
}
公共类MyKey
{
公共字符串MyBusinessKey
class ThrowHasCode: IEquatable<ThrowHasCode>
{
public int SomeVariable;
public virtual Equals(ThrowHasCode other)
{
return other.SomeVariable == SomeVariable;
}
public override int GetHashCode()
{
throw new ApplicationException("this class does not support GetHashCode and should not be used as a key for a dictionary");
}
}