问题:Unity 的 C# 脚本中,使用 enum 作字典 Dictionary<TKey,TValue> 的 key 时,key 的 Equals() 相等比较存在装箱开销。
解决:要避免 Key 相等比较引入的装箱开销,泛型字典的构造器提供了接受比较器 Comparer 的重载。因此,我们提供自己的 enum 比较器就行了。
进一步:自定义 enum 比较器很简单方便。只是,需要为用到的每个枚举类型都写个比较器,既然都差不多,能不能偷个懒呢 ^_^
下面考虑用下泛型,但由于 Enum 本身并没有实现 IEquatable<> 接口,直接调用其 Equals() 方法还是会装箱。那么,我们尝试下别的方式:
再次解决(部分):仅针对 int 类型的枚举。可以利用 int.GetHashCode() 就是其本身的特性,从而把 int 值的比较等价为其 HashCode 的比较,避开 Equals() 引入的装箱开销。(代码附后)
注意:这种方法仅是思路探索,如果想要实用,请自行做充分测试。
完整解决:微软 .Net 4.0 版本的泛型字典已经完整支持 enum key 了。所以,想要完美的体验,就努力升级吧。
ps:我用的 Unity 版本仅支持 .Net 2.0 版本 ,所以泛型字典还没有针对 enum key 做优化,但是我看到微软的 .Net 4.0 版本中已经针对 enum key 做了优化,至于最早是哪个 .Net 版本开始的优化,我并没有深入调查。这里的尝试也参考了 .Net 4.0 的优化。
代码示例:
// EnumKeyDictionary.cs
public class EnumKeyDictionary<TKey, TValue> : System.Collections.Generic.Dictionary<TKey, TValue> where TKey : struct
{
public EnumKeyDictionary() : base(GetComparer()) { }
public EnumKeyDictionary(int capacity) : base(capacity, GetComparer()) { }
public EnumKeyDictionary(IDictionary<TKey, TValue> dictionary) : base(dictionary, GetComparer()) { }
public EnumKeyDictionary(IEqualityComparer<TKey> comparer) : base(comparer) { }
public EnumKeyDictionary(int capacity, IEqualityComparer<TKey> comparer) : base(capacity, comparer) { }
public EnumKeyDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) : base(dictionary, comparer) { }
static IEqualityComparer<TKey> GetComparer()
{
System.Type typeHandle = typeof(TKey);
if (typeHandle.IsEnum)
{
// 利用 int.GetHashCode() 就是其本身
System.Type underlyingType = System.Enum.GetUnderlyingType(typeHandle);
if (underlyingType == typeof(int) ||
underlyingType == typeof(byte) ||
underlyingType == typeof(ushort) ||
underlyingType == typeof(uint))
{
return IntEnumEqualityComparer<TKey>.GetComparer();
}
}
return null;
}
}
// EqualityComparer.cs
public class IntEnumEqualityComparer<T> : IEqualityComparer<T> where T : struct
{
static IEqualityComparer<T> s_comparer;
public static IEqualityComparer<T> GetComparer()
{
if (s_comparer == null)
{
s_comparer = new IntEnumEqualityComparer<T>();
}
return s_comparer;
}
public bool Equals(T x, T y)
{
return x.GetHashCode() == y.GetHashCode();
}
public int GetHashCode(T obj)
{
return obj.GetHashCode();
}
}
// test.cs
using System;
using System.Collections.Generic;
public enum CKind
{
A,
B=1,
C=1,
D,
}
public class Test
{
static void Test0()
{
Dictionary<CKind, int> dic = new EnumKeyDictionary<CKind, int>();
}
}
C# 泛型比较器的代码片段:
.Net 2.0 版本
.Net 4.0 版本
Int32.GetHashCode