英文:
Does .NET Hashtable uses the specialized Font equality comparer when fonts are used as keys?
问题
我需要创建一组唯一的 Font 对象,每个对象都应该有一个关联的对象。这应该在一个 .NET Framework WinForms 应用程序中完成。这些 Font 对象来自不同的来源,不同的 Font 对象实际上可以是相同的 - 如果它们具有相同的名称、大小和其他特征。似乎可以使用 Hashtable 类来实现这一点。以下是一个在全新的 .NET Framework WinForms 应用程序中进行的简单测试:
private void Form1_Load(object sender, EventArgs e)
{
Font font1 = new Font(this.Font, FontStyle.Regular);
Font font2 = new Font("Microsoft Sans Serif", 8.25f);
Hashtable ht = new Hashtable();
ht.Add(font1, 1);
ht.Add(font2, 2);
}
尝试将 font2 添加到 Hashtable 时,会引发以下异常:
System.ArgumentException HResult=0x80070057 Message=Item has already been added. Key in dictionary: '[Font: Name=Microsoft Sans Serif, Size=8.25, Units=3, GdiCharSet=1, GdiVerticalFont=False]' Key being added: '[Font: Name=Microsoft Sans Serif, Size=8.25, Units=3, GdiCharSet=1, GdiVerticalFont=False]' Source=mscorlib StackTrace: at System.Collections.Hashtable.Insert(Object key, Object nvalue, Boolean add) at System.Collections.Hashtable.Add(Object key, Object value) at Font_Hashtable.Form1.Form1_Load(Object sender, EventArgs e)
这证明了专门的 Font 相等性比较器用于检查是否将 Font 对象用作 Hashtable 键。我想知道 - 我们真的可以在这种情况下使用 Hashtable 吗?据我所知,.NET Hashtable 如果它们是对象,应该通过引用来比较键,如果是这种情况,上面的代码不应该引发异常。
英文:
I need to create a set of unique Font objects each of which should have an associated object. This should be done in a .NET Framework WinForms app. These Font objects come from various sources, and different Font objects can be actually the same - if they have the same name, size, and other characteristics. It seems the Hashtable class can be used for this. Here is a simple test in a brand new WinForms app for .NET Framework:
<!-- language-all: lang-c# -->
private void Form1_Load(object sender, EventArgs e)
{
Font font1 = new Font(this.Font, FontStyle.Regular);
Font font2 = new Font("Microsoft Sans Serif", 8.25f);
Hashtable ht = new Hashtable();
ht.Add(font1, 1);
ht.Add(font2, 2);
}
The last statement that adds font2 throws the following exception:
> System.ArgumentException HResult=0x80070057 Message=Item has
> already been added. Key in dictionary: '[Font: Name=Microsoft Sans
> Serif, Size=8.25, Units=3, GdiCharSet=1, GdiVerticalFont=False]' Key
> being added: '[Font: Name=Microsoft Sans Serif, Size=8.25, Units=3,
> GdiCharSet=1, GdiVerticalFont=False]' Source=mscorlib StackTrace:
> at System.Collections.Hashtable.Insert(Object key, Object nvalue,
> Boolean add) at System.Collections.Hashtable.Add(Object key, Object
> value) at Font_Hashtable.Form1.Form1_Load(Object sender, EventArgs
> e)
This proves that the specialized Font equality comparer is used to check if Font objects are used as Hashtable keys. I wonder - can we really use a Hashtable for this scenario? As I know, .NET Hashtables should compare keys by references if they are objects, and there should be no exception in the code above if this is the case.
答案1
得分: 1
Hashtable
在Add
方法中检查键是否已经包含,如果是,则会引发此异常。因此,它首先检查是否存在具有相同哈希码的键。如果存在,它会检查它们是否相等,首先通过检查是否是相同的引用(object.ReferenceEquals
)。如果不是,则会检查它们是否相等。要么通过构造函数中提供的KeyComaprer
,要么通过object.Equals
。如果Equals
被重写,就会使用它。
文档也提到了这一点:
> "默认的哈希码提供程序是每个键的GetHashCode()
实现,而默认的比较器是每个键的Equals(Object)
实现。"
由于首先使用GetHashCode
,所以始终一起重写Equals
和GetHashCode
。两个相等的对象必须具有相同的哈希码(但相同的哈希码不一定意味着相等)。
另外,现在没有理由再使用旧的Hashtable
了。在这里,你应该使用Dictionary<Font, float>
。
无论你使用HashTable
还是泛型Dictionary<TKey, TValue>
,都不会影响这个问题:它们都会在Add
方法中检查是否有另一个具有相同哈希码的项,如果有,再检查两个项是否相等,然后引发ArgumentException
。
它们都使用传递的自定义IEqalityComparer
(构造函数)或Equals
(如果没有重写的话,就使用object.Equals
,它只比较引用)。
英文:
The Hashtable
checks in Add
if the key is already contained, then it will throw this exception. Therefore it checks if there is one with the same hashcode. If so it checks if they are equal, first by checking if it's the same same reference(object.ReferenceEquals
). If not it checks if they are equal. Either by the given KeyComaprer
in the constructor or by object.Equals
. If Equals
is overridden, Font
overrides Equals
, that one is used.
The documentation mentions it also:
> "The default hash code provider is each key's implementation of
> GetHashCode() and the default comparer is each key's implementation of
> Equals(Object)."
Since GetHashCode
is used first, always override Equals
and GetHashCode
together. Two equal objects must have the same hashcode(but same hashcode doesn't necessarily mean equal).
By the way, there is no reason to use the old Hashtable
anymore. Here you should use a Dictionary<Font, float>
.
If you use a HashTable
or a generic Dictionary<TKey, TValue>
does not make a difference for this issue: both are checking in Add
if there is another item with the same hash-code and if so, if both items are equal, then you get this ArgumentException
.
Both use either the passed custom IEqalityComparer
(constructor) or Equals
(if not overridden object.Equals
, which just compares references).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论