Using an enum as an array index in C#

前端 未结 10 802
死守一世寂寞
死守一世寂寞 2020-12-28 14:17

I want to do the same as in this question, that is:

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


        
相关标签:
10条回答
  • 2020-12-28 14:22

    If the values of your enum items are contigious, the array method works pretty well. However, in any case, you could use Dictionary<DayOfTheWeek, string> (which is less performant, by the way).

    0 讨论(0)
  • 2020-12-28 14:22

    You can always do some extra mapping to get an array index of an enum value in a consistent and defined way:

    int ArrayIndexFromDaysOfTheWeekEnum(DaysOfWeek day)
    {
       switch (day)
       {
         case DaysOfWeek.Sunday: return 0;
         case DaysOfWeek.Monday: return 1;
         ...
         default: throw ...;
       }
    }
    

    Be as specific as you can. One day someone will modify your enum and the code will fail because the enum's value was (mis)used as an array index.

    0 讨论(0)
  • 2020-12-28 14:23

    For future reference the above problem can be summarized as follows:

    I come from Delphi where you can define an array as follows:

    type
      {$SCOPEDENUMS ON}
      TDaysOfTheWeek = (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday);
    
      TDaysOfTheWeekStrings = array[TDaysOfTheWeek];
    

    Then you can iterate through the array using Min and Max:

    for Dow := Min(TDaysOfTheWeek) to Max(TDaysOfTheWeek) 
      DaysOfTheWeekStrings[Dow] := '';
    

    Though this is quite a contrived example, when you are dealing with array positions later in the code I can just type DaysOfTheWeekStrings[TDaysOfTheWeek.Monday]. This has the advantage of the fact that I should the TDaysOfTheWeek increase in size then I do not have to remember the new size of the array etc..... However back to the C# world. I have found this example C# Enum Array Example.

    0 讨论(0)
  • 2020-12-28 14:27

    You could make a class or struct that could do the work for you


    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]);
        }
    }
    

    EDIT

    For lack of a better place to put this, I am posting a generic version of the Caster class below. Unfortunately, it relies on runtime checks to enforce TKey as an enum.

    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]);
    
    0 讨论(0)
  • 2020-12-28 14:30

    If all you need is essentially a map, but don't want to incur performance overhead associated with dictionary lookups, this might work:

        public class EnumIndexedArray<TKey, T> : IEnumerable<KeyValuePair<TKey, T>> where TKey : struct
        {
            public EnumIndexedArray()
            {
                if (!typeof (TKey).IsEnum) throw new InvalidOperationException("Generic type argument is not an Enum");
                var size = Convert.ToInt32(Keys.Max()) + 1;
                Values = new T[size];
            }
    
            protected T[] Values;
    
            public static IEnumerable<TKey> Keys
            {
                get { return Enum.GetValues(typeof (TKey)).OfType<TKey>(); }
            }
    
            public T this[TKey index]
            {
                get { return Values[Convert.ToInt32(index)]; }
                set { Values[Convert.ToInt32(index)] = value; }
            }
    
            private IEnumerable<KeyValuePair<TKey, T>> CreateEnumerable()
            {
                return Keys.Select(key => new KeyValuePair<TKey, T>(key, Values[Convert.ToInt32(key)]));
            }
    
            public IEnumerator<KeyValuePair<TKey, T>> GetEnumerator()
            {
                return CreateEnumerable().GetEnumerator();
            }
    
            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
        }
    

    So in your case you could derive:

    class DaysOfWeekToStringsMap:EnumIndexedArray<DayOfWeek,string>{};
    

    Usage:

    var map = new DaysOfWeekToStringsMap();
    
    //using the Keys static property
    foreach(var day in DaysOfWeekToStringsMap.Keys){
        map[day] = day.ToString();
    }
    foreach(var day in DaysOfWeekToStringsMap.Keys){
        Console.WriteLine("map[{0}]={1}",day, map[day]);
    }
    
    // using iterator
    foreach(var value in map){
        Console.WriteLine("map[{0}]={1}",value.Key, value.Value);
    }
    

    Obviously this implementation is backed by an array, so non-contiguous enums like this:

    enum
    {
      Ok = 1,
      NotOk = 1000000
    }
    

    would result in excessive memory usage.

    If you require maximum possible performance you might want to make it less generic and loose all generic enum handling code I had to use to get it to compile and work. I didn't benchmark this though, so maybe it's no big deal.

    Caching the Keys static property might also help.

    0 讨论(0)
  • 2020-12-28 14:32

    Compact form of enum used as index and assigning whatever type to a Dictionary and strongly typed. In this case float values are returned but values could be complex Class instances having properties and methods and more:

    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];
    
    0 讨论(0)
提交回复
热议问题