.NET Hashtable 在使用字体作为键时是否使用专门的字体相等比较器?

huangapple go评论80阅读模式
英文:

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(&quot;Microsoft Sans Serif&quot;, 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

HashtableAdd方法中检查键是否已经包含,如果是,则会引发此异常。因此,它首先检查是否存在具有相同哈希码的键。如果存在,它会检查它们是否相等,首先通过检查是否是相同的引用(object.ReferenceEquals)。如果不是,则会检查它们是否相等。要么通过构造函数中提供的KeyComaprer,要么通过object.Equals。如果Equals被重写,就会使用它。

文档也提到了这一点:

> "默认的哈希码提供程序是每个键的GetHashCode()实现,而默认的比较器是每个键的Equals(Object)实现。"

由于首先使用GetHashCode,所以始终一起重写EqualsGetHashCode。两个相等的对象必须具有相同的哈希码(但相同的哈希码不一定意味着相等)。

另外,现在没有理由再使用旧的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&lt;Font, float&gt;.

If you use a HashTable or a generic Dictionary&lt;TKey, TValue&gt; 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).

huangapple
  • 本文由 发表于 2023年7月20日 21:54:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76730623.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定