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

发布于 2024-07-25 04:35:44 字数 376 浏览 5 评论 0原文

我想做与这个问题相同的事情,即:

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

...

Console.Write(custom_array[(int)DaysOfTheWeek.Sunday]);

然而,我宁愿有一些不可或缺的东西,而不是编写这个容易出错的代码。 C# 中是否有内置模块可以实现此目的?

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_enum];

...

Console.Write(custom_array[(int)DaysOfTheWeek.Sunday]);

however, I would rather have something integral to so, rather than write this error prone code. Is there a built in module in C# that does just this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

愛放△進行李 2024-08-01 04:35:45

如果您本质上需要的是一个映射,但不想产生与字典查找相关的性能开销,这可能会起作用:

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

所以在您的情况下,您可以得出:

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

用法:

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

显然这个实现是由数组支持的,所以是不连续的像这样的枚举:

enum
{
  Ok = 1,
  NotOk = 1000000
}

会导致内存使用过多。

如果您需要最大可能的性能,您可能希望使其不那么通用,并释放我必须用来编译和工作的所有通用枚举处理代码。 不过我没有对此进行基准测试,所以也许这没什么大不了的。

缓存 Keys 静态属性也可能有所帮助。

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.

一人独醉 2024-08-01 04:35:45

为了将来的参考,上述问题可以总结如下:

我来自 Delphi,您可以如下定义一个数组:

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

  TDaysOfTheWeekStrings = array[TDaysOfTheWeek];

然后您可以使用 Min 和 Max 迭代该数组:

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

虽然这是一个相当人为的示例,但当您处理稍后在代码中的数组位置我只需键入 DaysOfTheWeekStrings[TDaysOfTheWeek.Monday] 即可。 这样做的好处是,我应该增加 TDaysOfTheWeek 的大小,然后我就不必记住数组的新大小等......但是回到 C# 世界。 我找到了这个示例 C# 枚举数组示例

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.

§对你不离不弃 2024-08-01 04:35:45

我意识到这是一个老问题,但是有很多评论表明到目前为止所有解决方案都具有运行时检查以确保数据类型是枚举。 这是带有编译时检查的解决方案的完整解决方案(带有一些示例)(以及我的开发人员同事的一些评论和讨论)

//There is no good way to constrain a generic class parameter to an Enum.  The hack below does work at compile time,
//  though it is convoluted.  For examples of how to use the two classes EnumIndexedArray and ObjEnumIndexedArray,
//  see AssetClassArray below.  Or, e.g.
//      EConstraint.EnumIndexedArray<int, YourEnum> x = new EConstraint.EnumIndexedArray<int, YourEnum>();
//  See this post 
//      http://stackoverflow.com/questions/79126/create-generic-method-constraining-t-to-an-enum/29581813#29581813
// and the answer/comments by Julien Lebosquain
public class EConstraint : HackForCompileTimeConstraintOfTEnumToAnEnum<System.Enum> { }//THIS MUST BE THE ONLY IMPLEMENTATION OF THE ABSTRACT HackForCompileTimeConstraintOfTEnumToAnEnum
public abstract class HackForCompileTimeConstraintOfTEnumToAnEnum<SystemEnum> where SystemEnum : class
{
    //For object types T, users should use EnumIndexedObjectArray below.
    public class EnumIndexedArray<T, TEnum>
        where TEnum : struct, SystemEnum
    {
        //Needs to be public so that we can easily do things like intIndexedArray.data.sum()
        //   - just not worth writing up all the equivalent methods, and we can't inherit from T[] and guarantee proper initialization.
        //Also, note that we cannot use Length here for initialization, even if Length were defined the same as GetNumEnums up to
        //  static qualification, because we cannot use a non-static for initialization here.
        //  Since we want Length to be non-static, in keeping with other definitions of the Length property, we define the separate static
        //  GetNumEnums, and then define the non-static Length in terms of the actual size of the data array, just for clarity,
        //  safety and certainty (in case someone does something stupid like resizing data).
        public T[] data = new T[GetNumEnums()];

        //First, a couple of statics allowing easy use of the enums themselves.
        public static TEnum[] GetEnums()
        {
            return (TEnum[])Enum.GetValues(typeof(TEnum));
        }
        public TEnum[] getEnums()
        {
            return GetEnums();
        }
        //Provide a static method of getting the number of enums.  The Length property also returns this, but it is not static and cannot be use in many circumstances.
        public static int GetNumEnums()
        {
            return GetEnums().Length;
        }
        //This should always return the same as GetNumEnums, but is not static and does it in a way that guarantees consistency with the member array.
        public int Length { get { return data.Length; } }
        //public int Count  { get { return data.Length; } }

        public EnumIndexedArray() { }

        // [WDS 2015-04-17] Remove. This can be dangerous. Just force people to use EnumIndexedArray(T[] inputArray).
        // [DIM 2015-04-18] Actually, if you think about it, EnumIndexedArray(T[] inputArray) is just as dangerous:
        //   For value types, both are fine.  For object types, the latter causes each object in the input array to be referenced twice,
        //   while the former causes the single object t to be multiply referenced.  Two references to each of many is no less dangerous
        //   than 3 or more references to one. So all of these are dangerous for object types.
        //   We could remove all these ctors from this base class, and create a separate
        //         EnumIndexedValueArray<T, TEnum> : EnumIndexedArray<T, TEnum> where T: struct ...
        //   but then specializing to TEnum = AssetClass would have to be done twice below, once for value types and once
        //   for object types, with a repetition of all the property definitions.  Violating the DRY principle that much
        //   just to protect against stupid usage, clearly documented as dangerous, is not worth it IMHO.
        public EnumIndexedArray(T t)
        {
            int i = Length;
            while (--i >= 0)
            {
                this[i] = t;
            }
        }
        public EnumIndexedArray(T[] inputArray)
        {
            if (inputArray.Length > Length)
            {
                throw new Exception(string.Format("Length of enum-indexed array ({0}) to big. Can't be more than {1}.", inputArray.Length, Length));
            }
            Array.Copy(inputArray, data, inputArray.Length);
        }
        public EnumIndexedArray(EnumIndexedArray<T, TEnum> inputArray)
        {
            Array.Copy(inputArray.data, data, data.Length);
        }

        //Clean data access
        public T this[int ac] { get { return data[ac]; } set { data[ac] = value; } }
        public T this[TEnum ac] { get { return data[Convert.ToInt32(ac)]; } set { data[Convert.ToInt32(ac)] = value; } }
    }


    public class EnumIndexedObjectArray<T, TEnum> : EnumIndexedArray<T, TEnum>
        where TEnum : struct, SystemEnum
        where T : new()
    {
        public EnumIndexedObjectArray(bool doInitializeWithNewObjects = true)
        {
            if (doInitializeWithNewObjects)
            {
                for (int i = Length; i > 0; this[--i] = new T()) ;
            }
        }
        // The other ctor's are dangerous for object arrays
    }

    public class EnumIndexedArrayComparator<T, TEnum> : EqualityComparer<EnumIndexedArray<T, TEnum>>
        where TEnum : struct, SystemEnum
    {
        private readonly EqualityComparer<T> elementComparer = EqualityComparer<T>.Default;

        public override bool Equals(EnumIndexedArray<T, TEnum> lhs, EnumIndexedArray<T, TEnum> rhs)
        {
            if (lhs == rhs)
                return true;
            if (lhs == null || rhs == null)
                return false;

            //These cases should not be possible because of the way these classes are constructed.
            // HOWEVER, the data member is public, so somebody _could_ do something stupid and make 
            // data=null, or make lhs.data == rhs.data, even though lhs!=rhs (above check)
            //On the other hand, these are just optimizations, so it won't be an issue if we reomve them anyway,
            // Unless someone does something really dumb like setting .data to null or resizing to an incorrect size,
            // in which case things will crash, but any developer who does this deserves to have it crash painfully...
            //if (lhs.data == rhs.data)
            //    return true;
            //if (lhs.data == null || rhs.data == null)
            //    return false;

            int i = lhs.Length;
            //if (rhs.Length != i)
            //    return false;
            while (--i >= 0)
            {
                if (!elementComparer.Equals(lhs[i], rhs[i]))
                    return false;
            }
            return true;
        }
        public override int GetHashCode(EnumIndexedArray<T, TEnum> enumIndexedArray)
        {
            //This doesn't work: for two arrays ar1 and ar2, ar1.GetHashCode() != ar2.GetHashCode() even when ar1[i]==ar2[i] for all i (unless of course they are the exact same array object)
            //return engineArray.GetHashCode();
            //Code taken from comment by Jon Skeet - of course - in http://stackoverflow.com/questions/7244699/gethashcode-on-byte-array
            //31 and 17 are used commonly elsewhere, but maybe because everyone is using Skeet's post.
            //On the other hand, this is really not very critical.
            unchecked
            {
                int hash = 17;
                int i = enumIndexedArray.Length;
                while (--i >= 0)
                {
                    hash = hash * 31 + elementComparer.GetHashCode(enumIndexedArray[i]);
                }
                return hash;
            }
        }
    }
}

//Because of the above hack, this fails at compile time - as it should.  It would, otherwise, only fail at run time.
//public class ThisShouldNotCompile : EConstraint.EnumIndexedArray<int, bool>
//{
//}

//An example
public enum AssetClass { Ir, FxFwd, Cm, Eq, FxOpt, Cr };
public class AssetClassArrayComparator<T> : EConstraint.EnumIndexedArrayComparator<T, AssetClass> { }
public class AssetClassIndexedArray<T> : EConstraint.EnumIndexedArray<T, AssetClass>
{
    public AssetClassIndexedArray()
    {
    }
    public AssetClassIndexedArray(T t) : base(t)
    {
    }
    public AssetClassIndexedArray(T[] inputArray) :  base(inputArray)
    {
    }
    public AssetClassIndexedArray(EConstraint.EnumIndexedArray<T, AssetClass> inputArray) : base(inputArray)
    {
    }

    public T Cm    { get { return this[AssetClass.Cm   ]; } set { this[AssetClass.Cm   ] = value; } }
    public T FxFwd { get { return this[AssetClass.FxFwd]; } set { this[AssetClass.FxFwd] = value; } }
    public T Ir    { get { return this[AssetClass.Ir   ]; } set { this[AssetClass.Ir   ] = value; } }
    public T Eq    { get { return this[AssetClass.Eq   ]; } set { this[AssetClass.Eq   ] = value; } }
    public T FxOpt { get { return this[AssetClass.FxOpt]; } set { this[AssetClass.FxOpt] = value; } }
    public T Cr    { get { return this[AssetClass.Cr   ]; } set { this[AssetClass.Cr   ] = value; } }
}

//Inherit from AssetClassArray<T>, not EnumIndexedObjectArray<T, AssetClass>, so we get the benefit of the public access getters and setters above
public class AssetClassIndexedObjectArray<T> : AssetClassIndexedArray<T> where T : new()
{
    public AssetClassIndexedObjectArray(bool bInitializeWithNewObjects = true)
    {
        if (bInitializeWithNewObjects)
        {
            for (int i = Length; i > 0; this[--i] = new T()) ;
        }
    }
}

编辑:
如果您使用 C# 7.3 或更高版本,请不要使用这个丑陋的解决方案。 请参阅 Ian Goldby 2018 年的回答。

I realize this is an old question, but there have been a number of comments about the fact that all solutions so far have run-time checks to ensure the data type is an enum. Here is a complete solution (with some examples) of a solution with compile time checks (as well as some comments and discussions from my fellow developers)

//There is no good way to constrain a generic class parameter to an Enum.  The hack below does work at compile time,
//  though it is convoluted.  For examples of how to use the two classes EnumIndexedArray and ObjEnumIndexedArray,
//  see AssetClassArray below.  Or, e.g.
//      EConstraint.EnumIndexedArray<int, YourEnum> x = new EConstraint.EnumIndexedArray<int, YourEnum>();
//  See this post 
//      http://stackoverflow.com/questions/79126/create-generic-method-constraining-t-to-an-enum/29581813#29581813
// and the answer/comments by Julien Lebosquain
public class EConstraint : HackForCompileTimeConstraintOfTEnumToAnEnum<System.Enum> { }//THIS MUST BE THE ONLY IMPLEMENTATION OF THE ABSTRACT HackForCompileTimeConstraintOfTEnumToAnEnum
public abstract class HackForCompileTimeConstraintOfTEnumToAnEnum<SystemEnum> where SystemEnum : class
{
    //For object types T, users should use EnumIndexedObjectArray below.
    public class EnumIndexedArray<T, TEnum>
        where TEnum : struct, SystemEnum
    {
        //Needs to be public so that we can easily do things like intIndexedArray.data.sum()
        //   - just not worth writing up all the equivalent methods, and we can't inherit from T[] and guarantee proper initialization.
        //Also, note that we cannot use Length here for initialization, even if Length were defined the same as GetNumEnums up to
        //  static qualification, because we cannot use a non-static for initialization here.
        //  Since we want Length to be non-static, in keeping with other definitions of the Length property, we define the separate static
        //  GetNumEnums, and then define the non-static Length in terms of the actual size of the data array, just for clarity,
        //  safety and certainty (in case someone does something stupid like resizing data).
        public T[] data = new T[GetNumEnums()];

        //First, a couple of statics allowing easy use of the enums themselves.
        public static TEnum[] GetEnums()
        {
            return (TEnum[])Enum.GetValues(typeof(TEnum));
        }
        public TEnum[] getEnums()
        {
            return GetEnums();
        }
        //Provide a static method of getting the number of enums.  The Length property also returns this, but it is not static and cannot be use in many circumstances.
        public static int GetNumEnums()
        {
            return GetEnums().Length;
        }
        //This should always return the same as GetNumEnums, but is not static and does it in a way that guarantees consistency with the member array.
        public int Length { get { return data.Length; } }
        //public int Count  { get { return data.Length; } }

        public EnumIndexedArray() { }

        // [WDS 2015-04-17] Remove. This can be dangerous. Just force people to use EnumIndexedArray(T[] inputArray).
        // [DIM 2015-04-18] Actually, if you think about it, EnumIndexedArray(T[] inputArray) is just as dangerous:
        //   For value types, both are fine.  For object types, the latter causes each object in the input array to be referenced twice,
        //   while the former causes the single object t to be multiply referenced.  Two references to each of many is no less dangerous
        //   than 3 or more references to one. So all of these are dangerous for object types.
        //   We could remove all these ctors from this base class, and create a separate
        //         EnumIndexedValueArray<T, TEnum> : EnumIndexedArray<T, TEnum> where T: struct ...
        //   but then specializing to TEnum = AssetClass would have to be done twice below, once for value types and once
        //   for object types, with a repetition of all the property definitions.  Violating the DRY principle that much
        //   just to protect against stupid usage, clearly documented as dangerous, is not worth it IMHO.
        public EnumIndexedArray(T t)
        {
            int i = Length;
            while (--i >= 0)
            {
                this[i] = t;
            }
        }
        public EnumIndexedArray(T[] inputArray)
        {
            if (inputArray.Length > Length)
            {
                throw new Exception(string.Format("Length of enum-indexed array ({0}) to big. Can't be more than {1}.", inputArray.Length, Length));
            }
            Array.Copy(inputArray, data, inputArray.Length);
        }
        public EnumIndexedArray(EnumIndexedArray<T, TEnum> inputArray)
        {
            Array.Copy(inputArray.data, data, data.Length);
        }

        //Clean data access
        public T this[int ac] { get { return data[ac]; } set { data[ac] = value; } }
        public T this[TEnum ac] { get { return data[Convert.ToInt32(ac)]; } set { data[Convert.ToInt32(ac)] = value; } }
    }


    public class EnumIndexedObjectArray<T, TEnum> : EnumIndexedArray<T, TEnum>
        where TEnum : struct, SystemEnum
        where T : new()
    {
        public EnumIndexedObjectArray(bool doInitializeWithNewObjects = true)
        {
            if (doInitializeWithNewObjects)
            {
                for (int i = Length; i > 0; this[--i] = new T()) ;
            }
        }
        // The other ctor's are dangerous for object arrays
    }

    public class EnumIndexedArrayComparator<T, TEnum> : EqualityComparer<EnumIndexedArray<T, TEnum>>
        where TEnum : struct, SystemEnum
    {
        private readonly EqualityComparer<T> elementComparer = EqualityComparer<T>.Default;

        public override bool Equals(EnumIndexedArray<T, TEnum> lhs, EnumIndexedArray<T, TEnum> rhs)
        {
            if (lhs == rhs)
                return true;
            if (lhs == null || rhs == null)
                return false;

            //These cases should not be possible because of the way these classes are constructed.
            // HOWEVER, the data member is public, so somebody _could_ do something stupid and make 
            // data=null, or make lhs.data == rhs.data, even though lhs!=rhs (above check)
            //On the other hand, these are just optimizations, so it won't be an issue if we reomve them anyway,
            // Unless someone does something really dumb like setting .data to null or resizing to an incorrect size,
            // in which case things will crash, but any developer who does this deserves to have it crash painfully...
            //if (lhs.data == rhs.data)
            //    return true;
            //if (lhs.data == null || rhs.data == null)
            //    return false;

            int i = lhs.Length;
            //if (rhs.Length != i)
            //    return false;
            while (--i >= 0)
            {
                if (!elementComparer.Equals(lhs[i], rhs[i]))
                    return false;
            }
            return true;
        }
        public override int GetHashCode(EnumIndexedArray<T, TEnum> enumIndexedArray)
        {
            //This doesn't work: for two arrays ar1 and ar2, ar1.GetHashCode() != ar2.GetHashCode() even when ar1[i]==ar2[i] for all i (unless of course they are the exact same array object)
            //return engineArray.GetHashCode();
            //Code taken from comment by Jon Skeet - of course - in http://stackoverflow.com/questions/7244699/gethashcode-on-byte-array
            //31 and 17 are used commonly elsewhere, but maybe because everyone is using Skeet's post.
            //On the other hand, this is really not very critical.
            unchecked
            {
                int hash = 17;
                int i = enumIndexedArray.Length;
                while (--i >= 0)
                {
                    hash = hash * 31 + elementComparer.GetHashCode(enumIndexedArray[i]);
                }
                return hash;
            }
        }
    }
}

//Because of the above hack, this fails at compile time - as it should.  It would, otherwise, only fail at run time.
//public class ThisShouldNotCompile : EConstraint.EnumIndexedArray<int, bool>
//{
//}

//An example
public enum AssetClass { Ir, FxFwd, Cm, Eq, FxOpt, Cr };
public class AssetClassArrayComparator<T> : EConstraint.EnumIndexedArrayComparator<T, AssetClass> { }
public class AssetClassIndexedArray<T> : EConstraint.EnumIndexedArray<T, AssetClass>
{
    public AssetClassIndexedArray()
    {
    }
    public AssetClassIndexedArray(T t) : base(t)
    {
    }
    public AssetClassIndexedArray(T[] inputArray) :  base(inputArray)
    {
    }
    public AssetClassIndexedArray(EConstraint.EnumIndexedArray<T, AssetClass> inputArray) : base(inputArray)
    {
    }

    public T Cm    { get { return this[AssetClass.Cm   ]; } set { this[AssetClass.Cm   ] = value; } }
    public T FxFwd { get { return this[AssetClass.FxFwd]; } set { this[AssetClass.FxFwd] = value; } }
    public T Ir    { get { return this[AssetClass.Ir   ]; } set { this[AssetClass.Ir   ] = value; } }
    public T Eq    { get { return this[AssetClass.Eq   ]; } set { this[AssetClass.Eq   ] = value; } }
    public T FxOpt { get { return this[AssetClass.FxOpt]; } set { this[AssetClass.FxOpt] = value; } }
    public T Cr    { get { return this[AssetClass.Cr   ]; } set { this[AssetClass.Cr   ] = value; } }
}

//Inherit from AssetClassArray<T>, not EnumIndexedObjectArray<T, AssetClass>, so we get the benefit of the public access getters and setters above
public class AssetClassIndexedObjectArray<T> : AssetClassIndexedArray<T> where T : new()
{
    public AssetClassIndexedObjectArray(bool bInitializeWithNewObjects = true)
    {
        if (bInitializeWithNewObjects)
        {
            for (int i = Length; i > 0; this[--i] = new T()) ;
        }
    }
}

EDIT:
If you are using C# 7.3 or later, PLEASE don't use this ugly solution. See Ian Goldby's answer from 2018.

少女七分熟 2024-08-01 04:35:45

您始终可以进行一些额外的映射,以一致且定义的方式获取枚举值的数组索引:

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

尽可能具体。 有一天,有人会修改您的枚举,并且代码将失败,因为枚举的值被(错误)用作数组索引。

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.

给妤﹃绝世温柔 2024-08-01 04:35:45

这是 @ian-goldby 的一个非常好的答案,但它没有解决 @zar-shardan 提出的问题,这是我自己遇到的问题。 下面是我对解决方案的看法,其中有一个用于转换 IEnumerable 的扩展类,下面是一个测试类:

/// <summary>
/// An array indexed by an enumerated type instead of an integer
/// </summary>
public class ArrayIndexedByEnum<TKey, TElement> : IEnumerable<TElement> where TKey : Enum
{
  private readonly Array _array;
  private readonly Dictionary<TKey, TElement> _dictionary;

  /// <summary>
  /// Creates the initial array, populated with the defaults for TElement
  /// </summary>
  public ArrayIndexedByEnum()
  {
    var min = Convert.ToInt64(Enum.GetValues(typeof(TKey)).Cast<TKey>().Min());
    var max = Convert.ToInt64(Enum.GetValues(typeof(TKey)).Cast<TKey>().Max());
    var size = max - min + 1;

    // Check that we aren't creating a ridiculously big array, if we are,
    // then use a dictionary instead
    if (min >= Int32.MinValue && 
        max <= Int32.MaxValue && 
        size < Enum.GetValues(typeof(TKey)).Length * 3L)
    {
      var lowerBound = Convert.ToInt32(min);
      var upperBound = Convert.ToInt32(max);
      _array = Array.CreateInstance(typeof(TElement), new int[] {(int)size }, new int[] { lowerBound });
    }
    else
    {
      _dictionary = new Dictionary<TKey, TElement>();
      foreach (var value in Enum.GetValues(typeof(TKey)).Cast<TKey>())
      {
        _dictionary[value] = default(TElement);
      }
    }
  }

  /// <summary>
  /// Gets the element by enumerated type
  /// </summary>
  public TElement this[TKey key]
  {
    get => (TElement)(_array?.GetValue(Convert.ToInt32(key)) ?? _dictionary[key]);
    set
    {
      if (_array != null)
      {
        _array.SetValue(value, Convert.ToInt32(key));
      }
      else
      {
        _dictionary[key] = value;
      }
    }
  }

  /// <summary>
  /// Gets a generic enumerator
  /// </summary>
  public IEnumerator<TElement> GetEnumerator()
  {
    return Enum.GetValues(typeof(TKey)).Cast<TKey>().Select(k => this[k]).GetEnumerator();
  }

  System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
  {
    return GetEnumerator();
  }
}

这是扩展类:

/// <summary>
/// Extensions for converting IEnumerable<TElement> to ArrayIndexedByEnum
/// </summary>
public static class ArrayIndexedByEnumExtensions
{
  /// <summary>
  /// Creates a ArrayIndexedByEnumExtensions from an System.Collections.Generic.IEnumerable
  /// according to specified key selector and element selector functions.
  /// </summary>
  public static ArrayIndexedByEnum<TKey, TElement> ToArrayIndexedByEnum<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector) where TKey : Enum
  {
    var array = new ArrayIndexedByEnum<TKey, TElement>();
    foreach(var item in source)
    {
      array[keySelector(item)] = elementSelector(item);
    }
    return array;
  }
  /// <summary>
  /// Creates a ArrayIndexedByEnum from an System.Collections.Generic.IEnumerable
  /// according to a specified key selector function.
  /// </summary>
  public static ArrayIndexedByEnum<TKey, TSource> ToArrayIndexedByEnum<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) where TKey : Enum
  {
    return source.ToArrayIndexedByEnum(keySelector, i => i);
  }
}

这是我的测试:

[TestClass]
public class ArrayIndexedByEnumUnitTest
{
  private enum OddNumbersEnum : UInt16
  {
    One = 1,
    Three = 3,
    Five = 5,
    Seven = 7,
    Nine = 9
  }

  private enum PowersOf2 : Int64
  {
    TwoP0 = 1,
    TwoP1 = 2,
    TwoP2 = 4,
    TwoP3 = 8,
    TwoP4 = 16,
    TwoP5 = 32,
    TwoP6 = 64,
    TwoP7 = 128,
    TwoP8 = 256,
    TwoP9 = 512,
    TwoP10 = 1_024,
    TwoP11 = 2_048,
    TwoP12 = 4_096,
    TwoP13 = 8_192,
    TwoP14 = 16_384,
    TwoP15 = 32_768,
    TwoP16 = 65_536,
    TwoP17 = 131_072,
    TwoP18 = 262_144,
    TwoP19 = 524_288,
    TwoP20 = 1_048_576,
    TwoP21 = 2_097_152,
    TwoP22 = 4_194_304,
    TwoP23 = 8_388_608,
    TwoP24 = 16_777_216,
    TwoP25 = 33_554_432,
    TwoP26 = 67_108_864,
    TwoP27 = 134_217_728,
    TwoP28 = 268_435_456,
    TwoP29 = 536_870_912,
    TwoP30 = 1_073_741_824,
    TwoP31 = 2_147_483_648,
    TwoP32 = 4_294_967_296,
    TwoP33 = 8_589_934_592,
    TwoP34 = 17_179_869_184,
    TwoP35 = 34_359_738_368,
    TwoP36 = 68_719_476_736,
    TwoP37 = 137_438_953_472,
    TwoP38 = 274_877_906_944,
    TwoP39 = 549_755_813_888,
    TwoP40 = 1_099_511_627_776,
    TwoP41 = 2_199_023_255_552,
    TwoP42 = 4_398_046_511_104,
    TwoP43 = 8_796_093_022_208,
    TwoP44 = 17_592_186_044_416,
    TwoP45 = 35_184_372_088_832,
    TwoP46 = 70_368_744_177_664,
    TwoP47 = 140_737_488_355_328,
    TwoP48 = 281_474_976_710_656,
    TwoP49 = 562_949_953_421_312,
    TwoP50 = 1_125_899_906_842_620,
    TwoP51 = 2_251_799_813_685_250,
    TwoP52 = 4_503_599_627_370_500,
    TwoP53 = 9_007_199_254_740_990,
    TwoP54 = 18_014_398_509_482_000,
    TwoP55 = 36_028_797_018_964_000,
    TwoP56 = 72_057_594_037_927_900,
    TwoP57 = 144_115_188_075_856_000,
    TwoP58 = 288_230_376_151_712_000,
    TwoP59 = 576_460_752_303_423_000,
    TwoP60 = 1_152_921_504_606_850_000,
  }

  [TestMethod]
  public void TestSimpleArray()
  {
    var array = new ArrayIndexedByEnum<OddNumbersEnum, string>();

    var odds = Enum.GetValues(typeof(OddNumbersEnum)).Cast<OddNumbersEnum>().ToList();

    // Store all the values
    foreach (var odd in odds)
    {
      array[odd] = odd.ToString();
    }

    // Check the retrieved values are the same as what was stored
    foreach (var odd in odds)
    {
      Assert.AreEqual(odd.ToString(), array[odd]);
    }
  }

  [TestMethod]
  public void TestPossiblyHugeArray()
  {
    var array = new ArrayIndexedByEnum<PowersOf2, string>();

    var powersOf2s = Enum.GetValues(typeof(PowersOf2)).Cast<PowersOf2>().ToList();

    // Store all the values
    foreach (var powerOf2 in powersOf2s)
    {
      array[powerOf2] = powerOf2.ToString();
    }

    // Check the retrieved values are the same as what was stored
    foreach (var powerOf2 in powersOf2s)
    {
      Assert.AreEqual(powerOf2.ToString(), array[powerOf2]);
    }
  }
}

It was a very good answer by @ian-goldby, but it didn't address the issue raised by @zar-shardan, which is an issue I hit myself. Below is my take on a solution, with a an extension class for converting an IEnumerable, and a test class below that:

/// <summary>
/// An array indexed by an enumerated type instead of an integer
/// </summary>
public class ArrayIndexedByEnum<TKey, TElement> : IEnumerable<TElement> where TKey : Enum
{
  private readonly Array _array;
  private readonly Dictionary<TKey, TElement> _dictionary;

  /// <summary>
  /// Creates the initial array, populated with the defaults for TElement
  /// </summary>
  public ArrayIndexedByEnum()
  {
    var min = Convert.ToInt64(Enum.GetValues(typeof(TKey)).Cast<TKey>().Min());
    var max = Convert.ToInt64(Enum.GetValues(typeof(TKey)).Cast<TKey>().Max());
    var size = max - min + 1;

    // Check that we aren't creating a ridiculously big array, if we are,
    // then use a dictionary instead
    if (min >= Int32.MinValue && 
        max <= Int32.MaxValue && 
        size < Enum.GetValues(typeof(TKey)).Length * 3L)
    {
      var lowerBound = Convert.ToInt32(min);
      var upperBound = Convert.ToInt32(max);
      _array = Array.CreateInstance(typeof(TElement), new int[] {(int)size }, new int[] { lowerBound });
    }
    else
    {
      _dictionary = new Dictionary<TKey, TElement>();
      foreach (var value in Enum.GetValues(typeof(TKey)).Cast<TKey>())
      {
        _dictionary[value] = default(TElement);
      }
    }
  }

  /// <summary>
  /// Gets the element by enumerated type
  /// </summary>
  public TElement this[TKey key]
  {
    get => (TElement)(_array?.GetValue(Convert.ToInt32(key)) ?? _dictionary[key]);
    set
    {
      if (_array != null)
      {
        _array.SetValue(value, Convert.ToInt32(key));
      }
      else
      {
        _dictionary[key] = value;
      }
    }
  }

  /// <summary>
  /// Gets a generic enumerator
  /// </summary>
  public IEnumerator<TElement> GetEnumerator()
  {
    return Enum.GetValues(typeof(TKey)).Cast<TKey>().Select(k => this[k]).GetEnumerator();
  }

  System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
  {
    return GetEnumerator();
  }
}

Here's the extension class:

/// <summary>
/// Extensions for converting IEnumerable<TElement> to ArrayIndexedByEnum
/// </summary>
public static class ArrayIndexedByEnumExtensions
{
  /// <summary>
  /// Creates a ArrayIndexedByEnumExtensions from an System.Collections.Generic.IEnumerable
  /// according to specified key selector and element selector functions.
  /// </summary>
  public static ArrayIndexedByEnum<TKey, TElement> ToArrayIndexedByEnum<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector) where TKey : Enum
  {
    var array = new ArrayIndexedByEnum<TKey, TElement>();
    foreach(var item in source)
    {
      array[keySelector(item)] = elementSelector(item);
    }
    return array;
  }
  /// <summary>
  /// Creates a ArrayIndexedByEnum from an System.Collections.Generic.IEnumerable
  /// according to a specified key selector function.
  /// </summary>
  public static ArrayIndexedByEnum<TKey, TSource> ToArrayIndexedByEnum<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) where TKey : Enum
  {
    return source.ToArrayIndexedByEnum(keySelector, i => i);
  }
}

And here are my tests:

[TestClass]
public class ArrayIndexedByEnumUnitTest
{
  private enum OddNumbersEnum : UInt16
  {
    One = 1,
    Three = 3,
    Five = 5,
    Seven = 7,
    Nine = 9
  }

  private enum PowersOf2 : Int64
  {
    TwoP0 = 1,
    TwoP1 = 2,
    TwoP2 = 4,
    TwoP3 = 8,
    TwoP4 = 16,
    TwoP5 = 32,
    TwoP6 = 64,
    TwoP7 = 128,
    TwoP8 = 256,
    TwoP9 = 512,
    TwoP10 = 1_024,
    TwoP11 = 2_048,
    TwoP12 = 4_096,
    TwoP13 = 8_192,
    TwoP14 = 16_384,
    TwoP15 = 32_768,
    TwoP16 = 65_536,
    TwoP17 = 131_072,
    TwoP18 = 262_144,
    TwoP19 = 524_288,
    TwoP20 = 1_048_576,
    TwoP21 = 2_097_152,
    TwoP22 = 4_194_304,
    TwoP23 = 8_388_608,
    TwoP24 = 16_777_216,
    TwoP25 = 33_554_432,
    TwoP26 = 67_108_864,
    TwoP27 = 134_217_728,
    TwoP28 = 268_435_456,
    TwoP29 = 536_870_912,
    TwoP30 = 1_073_741_824,
    TwoP31 = 2_147_483_648,
    TwoP32 = 4_294_967_296,
    TwoP33 = 8_589_934_592,
    TwoP34 = 17_179_869_184,
    TwoP35 = 34_359_738_368,
    TwoP36 = 68_719_476_736,
    TwoP37 = 137_438_953_472,
    TwoP38 = 274_877_906_944,
    TwoP39 = 549_755_813_888,
    TwoP40 = 1_099_511_627_776,
    TwoP41 = 2_199_023_255_552,
    TwoP42 = 4_398_046_511_104,
    TwoP43 = 8_796_093_022_208,
    TwoP44 = 17_592_186_044_416,
    TwoP45 = 35_184_372_088_832,
    TwoP46 = 70_368_744_177_664,
    TwoP47 = 140_737_488_355_328,
    TwoP48 = 281_474_976_710_656,
    TwoP49 = 562_949_953_421_312,
    TwoP50 = 1_125_899_906_842_620,
    TwoP51 = 2_251_799_813_685_250,
    TwoP52 = 4_503_599_627_370_500,
    TwoP53 = 9_007_199_254_740_990,
    TwoP54 = 18_014_398_509_482_000,
    TwoP55 = 36_028_797_018_964_000,
    TwoP56 = 72_057_594_037_927_900,
    TwoP57 = 144_115_188_075_856_000,
    TwoP58 = 288_230_376_151_712_000,
    TwoP59 = 576_460_752_303_423_000,
    TwoP60 = 1_152_921_504_606_850_000,
  }

  [TestMethod]
  public void TestSimpleArray()
  {
    var array = new ArrayIndexedByEnum<OddNumbersEnum, string>();

    var odds = Enum.GetValues(typeof(OddNumbersEnum)).Cast<OddNumbersEnum>().ToList();

    // Store all the values
    foreach (var odd in odds)
    {
      array[odd] = odd.ToString();
    }

    // Check the retrieved values are the same as what was stored
    foreach (var odd in odds)
    {
      Assert.AreEqual(odd.ToString(), array[odd]);
    }
  }

  [TestMethod]
  public void TestPossiblyHugeArray()
  {
    var array = new ArrayIndexedByEnum<PowersOf2, string>();

    var powersOf2s = Enum.GetValues(typeof(PowersOf2)).Cast<PowersOf2>().ToList();

    // Store all the values
    foreach (var powerOf2 in powersOf2s)
    {
      array[powerOf2] = powerOf2.ToString();
    }

    // Check the retrieved values are the same as what was stored
    foreach (var powerOf2 in powersOf2s)
    {
      Assert.AreEqual(powerOf2.ToString(), array[powerOf2]);
    }
  }
}
穿透光 2024-08-01 04:35:44

如果枚举项的值是连续的,则数组方法效果很好。 但是,无论如何,您都可以使用 Dictionary (顺便说一句,性能较差)。

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).

jJeQQOZ5 2024-08-01 04:35:44

从 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();
  }
}

用法:

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

myArray[YourEnum.Other] = "World"; // compiler error

Since C# 7.3 it has been possible to use System.Enum as a constraint on type parameters. So the nasty hacks in the some of the other answers are no longer required.

Here's a very simple ArrayByEum class that does exactly what the question asked.

Note that it will waste space if the enum values are non-contiguous, and won't cope with enum values that are too large for an int. I did say this example was very simple.

/// <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();
  }
}

Usage:

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

myArray[YourEnum.Other] = "World"; // compiler error
静水深流 2024-08-01 04:35:44

您可以创建一个类或结构来为您完成工作


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

编辑

由于缺乏更好的地方来放置它,我在下面发布了 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]);

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]);
娇俏 2024-08-01 04:35:44

在这里:

string[] message_array = Enum.GetNames(typeof(DaysOfTheWeek));

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

string[] message_array = Enum.GetValues(typeof(DaysOfTheWeek));

Here you go:

string[] message_array = Enum.GetNames(typeof(DaysOfTheWeek));

If you really need the length, then just take the .Length on the result :)
You can get values with:

string[] message_array = Enum.GetValues(typeof(DaysOfTheWeek));
我不会写诗 2024-08-01 04:35:44

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

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

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];
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文