在C#中使用枚举作为数组索引

Nef*_*zen 27 c# indexing enums

我想在这个问题上做同样的事情,那就是:

enum DaysOfTheWeek {Sunday=0, Monday, Tuesday...};
string[] message_array = new string[number_of_items_at_enum];

...

Console.Write(custom_array[(int)DaysOfTheWeek.Sunday]);
Run Code Online (Sandbox Code Playgroud)

但是,我宁愿有一些不可或缺的东西,而不是写这个容易出错的代码.C#中是否有内置模块可以做到这一点?

Meh*_*ari 18

如果枚举项的值是有余的,则数组方法可以很好地工作.但是,在任何情况下,您都可以使用Dictionary<DayOfTheWeek, string>(顺便说一下,性能较差).

  • 如果您无法证明存在重大影响,那么字典解决方案是可取的.对数组进行预优化会产生两个问题:首先,它放弃了枚举的类型安全性,使其与静态类中定义的常量实际上没有差别(但更加人为).其次,你最终会编写更多的代码而不是更少的代码,以使其按照你想要的方式运行. (5认同)
  • @Spencer:我没说"重要".这是否重要取决于您的具体用法.它慢了吗?是的,毫无疑问,Dictionary比直接数组查找慢.这很重要吗?你应该基准测试看看. (2认同)

Ian*_*dby 10

从 C# 7.3 开始,可以将其System.Enum用作类型参数的约束。因此,不再需要其他一些答案中令人讨厌的黑客攻击。

这是一个非常简单的ArrayByEum类,它完全满足了问题的要求。

请注意,如果枚举值不连续,它将浪费空间,并且不会处理对于int. 我确实说过这个例子非常简单。

/// <summary>An array indexed by an Enum</summary>
/// <typeparam name="T">Type stored in array</typeparam>
/// <typeparam name="U">Indexer Enum type</typeparam>
public class ArrayByEnum<T,U> : IEnumerable where U : Enum // requires C# 7.3 or later
{
  private readonly T[] _array;
  private readonly int _lower;

  public ArrayByEnum()
  {
    _lower = Convert.ToInt32(Enum.GetValues(typeof(U)).Cast<U>().Min());
    int upper = Convert.ToInt32(Enum.GetValues(typeof(U)).Cast<U>().Max());
    _array = new T[1 + upper - _lower];
  }

  public T this[U key]
  {
    get { return _array[Convert.ToInt32(key) - _lower]; }
    set { _array[Convert.ToInt32(key) - _lower] = value; }
  }

  public IEnumerator GetEnumerator()
  {
    return Enum.GetValues(typeof(U)).Cast<U>().Select(i => this[i]).GetEnumerator();
  }
}
Run Code Online (Sandbox Code Playgroud)

用法:

ArrayByEnum<string,MyEnum> myArray = new ArrayByEnum<string,MyEnum>();
myArray[MyEnum.First] = "Hello";

myArray[YourEnum.Other] = "World"; // compiler error
Run Code Online (Sandbox Code Playgroud)


Mat*_*ted 7

你可以创建一个可以为你工作的类或结构


public class Caster
{
    public enum DayOfWeek
    {
        Sunday = 0,
        Monday,
        Tuesday,
        Wednesday,
        Thursday,
        Friday,
        Saturday
    }

    public Caster() {}
    public Caster(string[] data) { this.Data = data; }

    public string this[DayOfWeek dow]{
        get { return this.Data[(int)dow]; }
    }

    public string[] Data { get; set; }


    public static implicit operator string[](Caster caster) { return caster.Data; }
    public static implicit operator Caster(string[] data) { return new Caster(data); }

}

class Program
{
    static void Main(string[] args)
    {
        Caster message_array = new string[7];
        Console.Write(message_array[Caster.DayOfWeek.Sunday]);
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑

由于缺乏更好的地方,我在下面发布了Caster类的通用版本.不幸的是,它依赖于运行时检查来强制执行TKey作为枚举.

public enum DayOfWeek
{
    Weekend,
    Sunday = 0,
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday
}

public class TypeNotSupportedException : ApplicationException
{
    public TypeNotSupportedException(Type type)
        : base(string.Format("The type \"{0}\" is not supported in this context.", type.Name))
    {
    }
}

public class CannotBeIndexerException : ApplicationException
{
    public CannotBeIndexerException(Type enumUnderlyingType, Type indexerType)
        : base(
            string.Format("The base type of the enum (\"{0}\") cannot be safely cast to \"{1}\".",
                          enumUnderlyingType.Name, indexerType)
            )
    {
    }
}

public class Caster<TKey, TValue>
{
    private readonly Type baseEnumType;

    public Caster()
    {
        baseEnumType = typeof(TKey);
        if (!baseEnumType.IsEnum)
            throw new TypeNotSupportedException(baseEnumType);
    }

    public Caster(TValue[] data)
        : this()
    {
        Data = data;
    }

    public TValue this[TKey key]
    {
        get
        {
            var enumUnderlyingType = Enum.GetUnderlyingType(baseEnumType);
            var intType = typeof(int);
            if (!enumUnderlyingType.IsAssignableFrom(intType))
                throw new CannotBeIndexerException(enumUnderlyingType, intType);
            var index = (int) Enum.Parse(baseEnumType, key.ToString());
            return Data[index];
        }
    }

    public TValue[] Data { get; set; }


    public static implicit operator TValue[](Caster<TKey, TValue> caster)
    {
        return caster.Data;
    }

    public static implicit operator Caster<TKey, TValue>(TValue[] data)
    {
        return new Caster<TKey, TValue>(data);
    }
}

// declaring and using it.
Caster<DayOfWeek, string> messageArray =
    new[]
        {
            "Sunday",
            "Monday",
            "Tuesday",
            "Wednesday",
            "Thursday",
            "Friday",
            "Saturday"
        };
Console.WriteLine(messageArray[DayOfWeek.Sunday]);
Console.WriteLine(messageArray[DayOfWeek.Monday]);
Console.WriteLine(messageArray[DayOfWeek.Tuesday]);
Console.WriteLine(messageArray[DayOfWeek.Wednesday]);
Console.WriteLine(messageArray[DayOfWeek.Thursday]);
Console.WriteLine(messageArray[DayOfWeek.Friday]);
Console.WriteLine(messageArray[DayOfWeek.Saturday]);
Run Code Online (Sandbox Code Playgroud)


van*_*van 5

干得好:

string[] message_array = Enum.GetNames(typeof(DaysOfTheWeek));
Run Code Online (Sandbox Code Playgroud)

如果您确实需要长度,那么只需在结果上取 .Length :) 您可以通过以下方式获取值:

string[] message_array = Enum.GetValues(typeof(DaysOfTheWeek));
Run Code Online (Sandbox Code Playgroud)


tof*_*ofo 5

用作索引并将任何类型分配给字典和强类型的枚举的紧凑形式。在这种情况下,返回浮点值,但值可能是具有属性和方法等的复杂类实例:

enum opacityLevel { Min, Default, Max }
private static readonly Dictionary<opacityLevel, float> _oLevels = new Dictionary<opacityLevel, float>
{
    { opacityLevel.Max, 40.0 },
    { opacityLevel.Default, 50.0 },
    { opacityLevel.Min, 100.0 }
};

//Access float value like this
var x = _oLevels[opacitylevel.Default];
Run Code Online (Sandbox Code Playgroud)