伙计们,这对你来说是一个棘手的问题!
TickZoom系统的一部分必须将每种类型对象的实例收集到Dictionary <>类型中.
它们的相等和哈希代码必须基于对象的实例,这意味着引用相等而不是值相等.挑战在于系统中的某些对象已重写Equals()和GetHashCode()以用作值相等,其内部值将随时间而变化.这意味着他们的Equals和GetHashCode是无用的.如何解决这个问题而不是侵扰性?
到目前为止,我们创建了一个结构来包装每个名为ObjectHandle的对象,以便散列到Dictionary中.如下所示,我们实现了Equals(),但仍然存在如何计算哈希码的问题.
public struct ObjectHandle : IEquatable<ObjectHandle>{
public object Object;
public bool Equals(ObjectHandle other) {
return object.ReferenceEquals(this.Object,other.Object);
}
}
Run Code Online (Sandbox Code Playgroud)
看到?有一个方法object.ReferenceEquals(),它将比较引用相等性,而不考虑对象中任何被重写的Equals()实现.
现在,如何通过仅考虑引用而不考虑任何重写的GetHashCode()方法来计算匹配的GetHashCode()?
啊,我希望这给你一个有趣的谜题.我们被困在这里.
真诚的,韦恩
我正在阅读有效的C#并且有一条关于Object.GetHashCode()
我不明白的评论:
Object.GetHashCode()
使用类中的内部字段System.Object
来生成哈希值.创建时,为每个创建的对象分配一个唯一的对象键,存储为整数.
这些键从1开始,每次创建任何类型的新对象时都会递增.对象标识字段在System.Object
构造函数中设置,以后不能修改.Object.GetHashCode()
将此值作为给定对象的哈希码返回.
我试着查看文档,Object.GetHashCode()
但没有找到任何相关信息.
我编写了一段简单的代码来打印新生成的对象的哈希码:
using System;
namespace TestGetHashCode
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 100; i++)
{
object o = new object();
Console.WriteLine(o.GetHashCode());
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
打印的前几个数字是:
37121646,
45592480,
57352375,
2637164,
41014879,
3888474,
25209742,
26966483,
31884011
Run Code Online (Sandbox Code Playgroud)
哪个似乎不合适
这些键从1开始,每次创建任何类型的新对象时都会递增...
Object.GetHashCode()
返回此值
然后,为了找到这个"内部字段System.Object
"我尝试使用ReSharper反编译源,但我找到的代码是
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
[__DynamicallyInvokable]
public …
Run Code Online (Sandbox Code Playgroud) 我有两个客户端IPAddress
从同一个 创建实例byte[]
并通过WCF(使用DataContractSerializer
)发送到服务器.
在服务器上,这些IPAddress
实例作为键插入字典中,但由于某种原因,它们被添加为不同的键.
记录时我发现它们相同但GetHashCode
返回不同的结果.
var client1Address = // sent from client1
var client2Address = // sent from client2
Console.WriteLine(client1Address.Equals(client2Address));
Console.WriteLine(client1Address.GetHashCode().Equals(client2Address.GetHashCode()));
Run Code Online (Sandbox Code Playgroud)
输出:
true
false
Run Code Online (Sandbox Code Playgroud)
等于IPAddress
实例如何返回不同的GetHashCode
结果?
嘿所有,我一直在阅读实现.NET中对象的GetHashCode()覆盖的最佳方法,并且我遇到的大多数答案涉及以某种方式将来自数字类型的成员的数字混合在一起来提出方法.问题是,我有一个使用字母数字字符串作为其键的对象,我想知道是否有一些根本错误的东西只是使用内部ID作为键的字符串的对象,如下所示?
// Override GetHashCode() to return a permanent, unique identifier for
// this object.
static private int m_next_hash_id = 1;
private int m_hash_code = 0;
public override int GetHashCode() {
if (this.m_hash_code == 0)
this.m_hash_code = <type>.m_next_hash_id++;
return this.m_hash_code;
}
Run Code Online (Sandbox Code Playgroud)
是否有更好的方法为使用字母数字字符串作为键的对象提供唯一的哈希码?(不,字母数字字符串的数字部分不是唯一的;其中一些字符串实际上根本没有数字.)任何想法都将不胜感激!
从Artech的博客看,然后我们在评论中进行了讨论.由于该博客仅以中文撰写,我在此处作简要说明.代码重现:
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public abstract class BaseAttribute : Attribute
{
public string Name { get; set; }
}
public class FooAttribute : BaseAttribute { }
[Foo(Name = "A")]
[Foo(Name = "B")]
[Foo(Name = "C")]
public class Bar { }
//Main method
var attributes = typeof(Bar).GetCustomAttributes(true).OfType<FooAttribute>().ToList<FooAttribute>();
var getC = attributes.First(item => item.Name == "C");
attributes.Remove(getC);
attributes.ForEach(a => Console.WriteLine(a.Name));
Run Code Online (Sandbox Code Playgroud)
代码全部获取FooAttribute
并删除名称为"C"的代码.显然输出是"A"和"B"?如果一切顺利,你就不会看到这个问题.事实上,理论上你会得到"AC""BC"甚至是"AB"(我的机器上有AC,博客作者有BC).问题源于System.Attribute中GetHashCode/Equals的实现.实现的片段:
[SecuritySafeCritical]
public override int GetHashCode()
{
Type type = base.GetType();
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)//*****NOTICE***** FieldInfo[] fields …
我尝试了一批随机字符串,我得到的所有值都是正数,但我想知道:
将 String.GetHashCode()
返回负数还是0?
由于返回值是int,所以我猜它可能是,所以如果是这种情况,我必须改变我的逻辑.
如果您有答案或有一些官方消息来源,请分享
VS2005文档重载Equals()和Operator ==(C#编程指南)的指南部分说明
不建议在非不可变类型中覆盖operator ==.
较新的.NET Framework 4文档实现等于和等于运算符的指南(==)省略了该语句,尽管社区内容中的一篇帖子重复了断言并引用了旧文档.
似乎至少对于一些琐碎的可变类来重写Equals()是合理的,例如
public class ImaginaryNumber
{
public double RealPart { get; set; }
public double ImaginaryPart { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
在数学中,具有相同实部和相同虚部的两个虚数实际上在测试相等性的时间点是相等的.声明它们不相等是不正确的,如果具有相同RealPart和ImaginaryPart的单独对象未被覆盖Equals(),则会发生这种情况.
另一方面,如果一个重写Equals(),则还应该重写GetHashCode().如果将覆盖Equals()和GetHashCode()的ImaginaryNumber放在HashSet中,并且可变实例更改其值,则不再在HashSet中找到该对象.
MSDN是否不正确删除有关不覆盖Equals()
和operator==
非不可变类型的指南?
为可变类型重写Equals()是否合理,其中"在现实世界中"所有属性的等价意味着对象本身是相等的(如同ImaginaryNumber
)?
如果它是合理的,当对象实例参与HashSet或依赖于GetHashCode()的其他东西没有改变时,如何最好地处理潜在的可变性?
UPDATE
通常,当期望将类型的对象添加到某种类型的集合时,或者当它们的主要目的是存储一组字段或属性时,实现值相等.您可以根据类型中所有字段和属性的比较来定义值相等,或者可以将定义基于子集.但无论是哪种情况,还是在类和结构中,您的实现都应遵循等效的五个保证:
为了寻找词典的快速复合键,我遇到异常,我无法理解也无法证明.
在有限的测试中
Dictionary<KeyValuePair<UInt32, UInt32>, string>
Run Code Online (Sandbox Code Playgroud)
明显慢于(200:1)
Dictionary<KeyValuePair<UInt16, UInt16>, string>
Run Code Online (Sandbox Code Playgroud)
测试两个循环,从0到1000 Populate,然后包含ContainsKey
Poplulate ContainsKey
UInt32 92085 86578
UInt16 2201 431
Run Code Online (Sandbox Code Playgroud)
问题是
new KeyValuePair<UInt32, UInt32>(i, j).GetHashCode();
Run Code Online (Sandbox Code Playgroud)
产生许多重复.
在循环i和j 1024中,仅创建1024个唯一散列值.
基于来自CasperOne的雪崩评论尝试了i*31和j*97(两个素数),这导致105280在1024X1024上独一无二.仍然有很多重复.CasperOne我知道这与随机不一样.但随机输入并不是我的工作.GetHashCode()应该随机化输出.
为什么重复次数很多?
相同的循环
new KeyValuePair<UInt16, UInt16>(i, j).GetHashCode();
Run Code Online (Sandbox Code Playgroud)
产生1024 X 1024唯一哈希码(完美).
Int32有同样的问题.
这些重复哈希值会终止
Dictionary<KeyValuePair<UInt32, UInt32>, string>
Run Code Online (Sandbox Code Playgroud)
与Int16相比,元组还会生成很多重复项,在Int32中不会降级.
生成原始KVP和原始KPV.GetHashCode的时间类似.
与HashSet相同的异常.
Dictionary<KeyValuePair<UInt32, UInt32>, string> dKVPu32 = new Dictionary<KeyValuePair<UInt32, UInt32>, string>();
Dictionary<KeyValuePair<UInt16, UInt16>, string> dKVPu16 = new Dictionary<KeyValuePair<UInt16, UInt16>, string>();
KeyValuePair<UInt32, UInt32> kvpUint32;
KeyValuePair<UInt16, UInt16> kvpUint16;
int range = 1000;
Int32 hashCode;
HashSet<Int32> kvpUint32Hash = new HashSet<Int32>(); …
Run Code Online (Sandbox Code Playgroud) 在阅读StackOverflow上有关覆盖的所有问题和答案之后,GetHashCode()
我编写了以下扩展方法,以便轻松方便地覆盖GetHashCode()
:
public static class ObjectExtensions
{
private const int _seedPrimeNumber = 691;
private const int _fieldPrimeNumber = 397;
public static int GetHashCodeFromFields(this object obj, params object[] fields) {
unchecked { //unchecked to prevent throwing overflow exception
int hashCode = _seedPrimeNumber;
for (int i = 0; i < fields.Length; i++)
if (fields[i] != null)
hashCode *= _fieldPrimeNumber + fields[i].GetHashCode();
return hashCode;
}
}
}
Run Code Online (Sandbox Code Playgroud)
(我基本上只重构了有人在那里发布的代码,因为我真的很喜欢它可以一般使用)
我用的是这样的:
public override int GetHashCode() {
return this.GetHashCodeFromFields(field1, field2, field3);
}
Run Code Online (Sandbox Code Playgroud)
你看到这段代码有什么问题吗?
我想知道.Net HashSet<T>
是完全基于哈希码还是它是否也使用了相等?
我有一个特定的类,我可能会实例化数百万个实例,并且有一些合理的可能性,在这一点上一些哈希码会发生冲突.
我正在考虑使用HashSet来存储这个类的一些实例,我想知道它是否真的值得做 - 如果一个元素的唯一性只是根据它的哈希码确定那么那对我来说对于实际的应用程序毫无用处
MSDN文档在这个主题上似乎相当模糊 - 任何启示都会受到赞赏
gethashcode ×10
c# ×9
.net ×5
equals ×3
hash ×3
attributes ×1
c#-2.0 ×1
dictionary ×1
hashcode ×1
hashset ×1
ip-address ×1
object ×1
reference ×1
string ×1