不带装箱的 System.Enum 字典的比较器

MrI*_*ito 1 c# garbage-collection unity-game-engine

当我尝试创建字典时遇到问题,其中键是 System.Enum。问题是,像这样的字典会分配垃圾,因为默认的 EqualityComparer 不是最好的之一。我尝试编写自己的比较器,但没有成功。这有可能吗?

    public enum MyEnum
{
    One, Two, Three
}

public Dictionary<Enum, string> dict = new Dictionary<Enum, string>();

public void Test()
{
    this.dict.Add(MyEnum.One, "One");
    this.dict.Add(MyEnum.Two, "Two");
    this.dict.Add(MyEnum.Three, "Three");

    string result;
    this.dict.TryGetValue(MyEnum.Two, out result); // <-- memory alocation :-(
}
Run Code Online (Sandbox Code Playgroud)

Exe*_*tor 5

没有装箱,没有堆分配。非常快。无需为每个枚举编写特定的比较器。

该版本适用于任何枚举,只要其基础类型不超过 32 位(因此 byte、ushort、uint 都可以)。

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

public sealed class EnumComparer<T> : IEqualityComparer<T>
{
    [StructLayout(LayoutKind.Explicit)]
    private struct Transformer
    {
        [FieldOffset(0)]
        public T t;

        [FieldOffset(0)]
        public int int32;
    }

    public static EnumComparer<T> Default { get; } = new EnumComparer<T>();

    private EnumComparer()
    {
    }

    public bool Equals(T a, T b)
    {
        Transformer aTransformer = new Transformer { t = a };
        Transformer bTransformer = new Transformer { t = b };
        return aTransformer.int32 == bTransformer.int32;
    }

    public int GetHashCode(T value)
    {
        Transformer valueTransformer = new Transformer { t = value };
        return valueTransformer.int32.GetHashCode();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你想将它与数组一起使用,你可能需要创建一些扩展方法,然后你可以像这样使用它:

bool contained = enumArray.Contains(MyEnum.someValue, EnumComparer<MyEnum>.Default);


public static class Extensions
{
    public static bool Contains<T>(this T[] array, T value, IEqualityComparer<T> equalityComparer)
    {
        if (array == null)
        {
            throw new ArgumentNullException("array");
        }
        return Extensions.IndexOf<T>(array, value, 0, array.Length, equalityComparer) >= 0;
    }

    public static int IndexOf<T>(this T[] array, T value, IEqualityComparer<T> equalityComparer)
    {
        if (array == null)
        {
            throw new ArgumentNullException("array");
        }
        return Extensions.IndexOf<T>(array, value, 0, array.Length, equalityComparer);
    }

    public static int IndexOf<T>(this T[] array, T value, int startIndex, int count, IEqualityComparer<T> equalityComparer)
    {
        if (array == null)
        {
            throw new ArgumentNullException("array");
        }
        if (count < 0 || startIndex < array.GetLowerBound(0) || startIndex - 1 > array.GetUpperBound(0) - count)
        {
            throw new ArgumentOutOfRangeException();
        }

        int num = startIndex + count;
        for (int i = startIndex; i < num; i++)
        {
            if (equalityComparer.Equals(array[i], value))
            {
                return i;
            }
        }
        return -1;
    }
}
Run Code Online (Sandbox Code Playgroud)