这个问题似乎正在寻找检索映射到Enum
常量的项目的最快方法。
几乎所有类型的底层类型Enum
(不是位字段(即未声明为[Flags]
))都是 32 位有符号整数。这有合理的性能原因。使用不同的东西的唯一真正原因是如果你绝对必须尽量减少内存使用。位域是另一回事,但我们在这里不关心它们。
在这种典型情况下,数组映射是理想的(通常比 a 更快switch
)。这是一些简洁且针对检索进行优化的通用代码。不幸的是,由于 .NET 通用约束的限制,需要一些技巧(例如必须将强制转换委托传递给实例构造函数)。
using System;
using System.Runtime.CompilerServices;
namespace DEMO
{
public sealed class EnumMapper<TKey, TValue> where TKey : struct, IConvertible
{
private struct FlaggedValue<T>
{
public bool flag;
public T value;
}
private static readonly int size;
private readonly Func<TKey, int> func;
private FlaggedValue<TValue>[] flaggedValues;
public TValue this[TKey key]
{
get
{
int index = this.func.Invoke(key);
FlaggedValue<TValue> flaggedValue = this.flaggedValues[index];
if (flaggedValue.flag == false)
{
EnumMapper<TKey, TValue>.ThrowNoMappingException(); // Don't want the exception code in the method. Make this callsite as small as possible to promote JIT inlining and squeeze out every last bit of performance.
}
return flaggedValue.value;
}
}
static EnumMapper()
{
Type keyType = typeof(TKey);
if (keyType.IsEnum == false)
{
throw new Exception("The key type [" + keyType.AssemblyQualifiedName + "] is not an enumeration.");
}
Type underlyingType = Enum.GetUnderlyingType(keyType);
if (underlyingType != typeof(int))
{
throw new Exception("The key type's underlying type [" + underlyingType.AssemblyQualifiedName + "] is not a 32-bit signed integer.");
}
var values = (int[])Enum.GetValues(keyType);
int maxValue = 0;
foreach (int value in values)
{
if (value < 0)
{
throw new Exception("The key type has a constant with a negative value.");
}
if (value > maxValue)
{
maxValue = value;
}
}
EnumMapper<TKey, TValue>.size = maxValue + 1;
}
public EnumMapper(Func<TKey, int> func)
{
if (func == null)
{
throw new ArgumentNullException("func",
"The func cannot be a null reference.");
}
this.func = func;
this.flaggedValues = new FlaggedValue<TValue>[EnumMapper<TKey, TValue>.size];
}
public static EnumMapper<TKey, TValue> Construct(Func<TKey, int> func)
{
return new EnumMapper<TKey, TValue>(func);
}
public EnumMapper<TKey, TValue> Map(TKey key,
TValue value)
{
int index = this.func.Invoke(key);
FlaggedValue<TValue> flaggedValue;
flaggedValue.flag = true;
flaggedValue.value = value;
this.flaggedValues[index] = flaggedValue;
return this;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ThrowNoMappingException()
{
throw new Exception("No mapping exists corresponding to the key.");
}
}
}
然后,您可以使用漂亮的流畅界面简单地初始化映射:
var mapper = EnumMapper<EnumType, ValueType>.Construct((x) => (int)x)
.Map(EnumType.Constant1, value1)
.Map(EnumType.Constant2, value2)
.Map(EnumType.Constant3, value3)
.Map(EnumType.Constant4, value4)
.Map(EnumType.Constant5, value5);
并轻松检索映射值:
ValueType value = mapper[EnumType.Constant3];
检索方法的 x86 程序集(使用 Visual Studio 2013 编译器生成)是最小的:
000007FE8E9909B0 push rsi
000007FE8E9909B1 sub rsp,20h
000007FE8E9909B5 mov rsi,rcx
000007FE8E9909B8 mov rax,qword ptr [rsi+8]
000007FE8E9909BC mov rcx,qword ptr [rax+8]
000007FE8E9909C0 call qword ptr [rax+18h] // The casting delegate's callsite is optimised to just two instructions
000007FE8E9909C3 mov rdx,qword ptr [rsi+10h]
000007FE8E9909C7 mov ecx,dword ptr [rdx+8]
000007FE8E9909CA cmp eax,ecx
000007FE8E9909CC jae 000007FE8E9909ED
000007FE8E9909CE movsxd rax,eax
000007FE8E9909D1 lea rax,[rdx+rax*8+10h]
000007FE8E9909D6 movzx edx,byte ptr [rax]
000007FE8E9909D9 mov esi,dword ptr [rax+4]
000007FE8E9909DC test dl,dl
000007FE8E9909DE jne 000007FE8E9909E5
000007FE8E9909E0 call 000007FE8E9901B8
000007FE8E9909E5 mov eax,esi
000007FE8E9909E7 add rsp,20h
000007FE8E9909EB pop rsi
000007FE8E9909EC ret
000007FE8E9909ED call 000007FEEE411A08
000007FE8E9909F2 int 3