当Font作为键时,.NET Hashtable是否使用专用相等比较器及可用性疑问
问题场景
在.NET Framework WinForms应用中,需要创建一个关联对应对象的唯一Font集合,要求属性(名称、大小等)相同的Font视为同一对象,尝试用Hashtable实现,测试代码如下:
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的语句时抛出异常:
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)
疑惑点:原本认为Hashtable对对象键是按引用比较,但实际却把属性相同的不同Font实例视为重复键,不确定该场景是否还能使用Hashtable。
异常原因解析
Hashtable的键比较逻辑并非固定为引用比较,而是依赖于键对象自身的Equals()和GetHashCode()实现:
Font类重写了Equals()和GetHashCode()方法,会根据字体的名称、大小、样式等属性值来判断相等性,而非引用地址。- 测试中的
font1和font2属性完全一致,所以Hashtable判定它们是同一个键,抛出重复添加的异常。
场景适用性结论
这个场景完全可以使用Hashtable,甚至它的默认行为正好匹配你的需求:
- 你需要“属性相同的Font视为同一对象”,而Hashtable利用
Font自身的内容相等逻辑,自动帮你过滤了属性重复的Font实例,避免集合中出现属性一致的重复项。 - 如果后续需要修改比较规则(比如改为引用比较),可以自定义实现
IEqualityComparer接口,在创建Hashtable时传入该比较器实例,覆盖默认的相等判断逻辑。
内容的提问来源于stack exchange,提问作者Tecman

