如何避免枚举通过Equals跟int类型做对比会装箱的问题。
为什么会产生装箱?
不管值类型还是枚举类型,它们的Equals的参数类型都是object。
微软的各种类型示例的比较的指导中提到:
对象比较值时用ReferenceEquals或者Equals;
值类型比较时直接利用=等号即可。
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/equality-comparisons
.net和旧版的mono以enum作为字典key装箱的原因也是于此。
机制:
ContainsKey、TryGetValue或在字典中使用数组表示法查找字典中的项时,Dictionary将使用 EqualComparer
新版的mono框架提供了对enum与int做对比的新的比较器EnumIntEqualityComparer:
它将通过写int数组的方式将枚举转为int类型后在做等号的对比。这样就避免的调用Equals的装箱。
(解决方案)如何避免枚举间或者枚举跟int做对比时,如何避免装箱?
我想答案已很浅显了。
就是不要直接使用Enum.Equals,而是先转为int再通过=等号做对比。
转换int可以利用如下代码:
public class EnumInt32ToInt
{
public static int Convert<TEnum>(TEnum value) where TEnum : struct
{
return ArrayUtils.UnsafeMov<TEnum, int>(value);
}
}
若采用了.net框架,以enum作为字典key。则可以自定义个比较器传入字典中。可以直接抄mono的EnumEqualityComparer(利用ArrayUtils.UnsafeMov转int方式)。
或者强行转换int类型的方式:
public struct MyEnumCOmparer : IEqualityComparer<MyEnum>
{
public bool Equals(MyEnum x, MyEnum y)
{
return x == y;
}
public int GetHashCode(MyEnum obj)
{
// you need to do some thinking here,
return (int)obj;
}
}
通过Expression.ConvertChecked转为int的方式。
struct FastEnumIntEqualityComparer<TEnum> : IEqualityComparer<TEnum>
where TEnum : struct
{
static class BoxAvoidance
{
static readonly Func<TEnum, int> _wrapper;
public static int ToInt(TEnum enu) //(这里是使用一个c#编译为il库, 可以使用C# Union转化 或者 unsafe 直接使用指针)
{
return _wrapper(enu);
}
static BoxAvoidance()
{
var p = Expression.Parameter(typeof(TEnum), null);
var c = Expression.ConvertChecked(p, typeof(int));
_wrapper = Expression.Lambda<Func<TEnum, int>>(c, p).Compile();
}
}
public bool Equals(TEnum firstEnum, TEnum secondEnum)
{
return BoxAvoidance.ToInt(firstEnum) ==
BoxAvoidance.ToInt(secondEnum);
}
public int GetHashCode(TEnum firstEnum)
{
return BoxAvoidance.ToInt(firstEnum);
}
}
通过il code的方式也行
.assembly extern mscorlib
{
.ver 0:0:0:0
}
.assembly 'enum2int'
{
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.class public auto ansi beforefieldinit EnumInt32ToInt
extends [mscorlib]System.Object
{
.method public hidebysig static int32 Convert<valuetype
.ctor ([mscorlib]System.ValueType) TEnum>(!!TEnum 'value') cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_000b: ret
}
}
参考:
《dictionary enum key how to avoid GC》
https://www.cnblogs.com/herenzhiming/articles/9245531.html
《EqualityComparar源码》
https://github.com/kitsilanosoftware/mono/blob/7008bb686727e4e18cafbbdb11ced4a463ddcbfb/mcs/class/corlib/System.Collections.Generic/EqualityComparer.cs#L186