español - enumeracion en c#
Usar una enumeración como índice de matriz en C# (9)
Quiero hacer lo mismo que en esta pregunta , es decir:
enum DaysOfTheWeek {Sunday=0, Monday, Tuesday...};
string[] message_array = new string[number_of_items_at_enum];
...
Console.Write(custom_array[(int)DaysOfTheWeek.Sunday]);
sin embargo, prefiero tener algo integral para eso, en lugar de escribir este código propenso a errores. ¿Hay un módulo integrado en C # que hace esto?
Aqui tienes:
string[] message_array = Enum.GetNames(typeof(DaysOfTheWeek));
Si realmente necesita la longitud, simplemente tome el .Length en el resultado :) Puede obtener valores con:
string[] message_array = Enum.GetValues(typeof(DaysOfTheWeek));
Desde C # 7.3, ha sido posible usar System.Enum
como una restricción en los parámetros de tipo . De modo que ya no son necesarios los desagradables ataques en algunas de las otras respuestas.
Aquí hay una clase ArrayByEum
muy simple que hace exactamente lo que hizo la pregunta.
Tenga en cuenta que desperdiciará espacio si los valores enum no son contiguos, y no soportará los valores enum que son demasiado grandes para un int
. Dije que este ejemplo fue muy 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();
}
}
Uso:
ArrayByEnum<string,MyEnum> myArray = new ArrayByEnum<string,MyEnum>();
myArray[MyEnum.First] = "Hello";
myArray[YourEnum.Other] = "World"; // compiler error
Forma compacta de enumeración utilizada como índice y asignación de cualquier tipo a un diccionario y fuertemente tipada. En este caso, se devuelven valores flotantes pero los valores pueden ser instancias de clase complejas que tienen propiedades y métodos, y más:
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];
Me doy cuenta de que esta es una pregunta antigua, pero ha habido una serie de comentarios sobre el hecho de que todas las soluciones hasta ahora tienen comprobaciones de tiempo de ejecución para garantizar que el tipo de datos sea una enumeración. Aquí hay una solución completa (con algunos ejemplos) de una solución con comprobaciones de tiempo de compilación (así como algunos comentarios y discusiones de mis compañeros desarrolladores)
//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://.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://.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()) ;
}
}
}
Para referencia futura, el problema anterior se puede resumir de la siguiente manera:
Vengo de Delphi, donde puedes definir una matriz de la siguiente manera:
type
{$SCOPEDENUMS ON}
TDaysOfTheWeek = (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday);
TDaysOfTheWeekStrings = array[TDaysOfTheWeek];
Luego puede iterar a través de la matriz usando Min y Max:
for Dow := Min(TDaysOfTheWeek) to Max(TDaysOfTheWeek)
DaysOfTheWeekStrings[Dow] := '''';
Aunque este es un buen ejemplo, cuando se trata de posiciones de arreglos más adelante en el código, simplemente puedo escribir DaysOfTheWeekStrings[TDaysOfTheWeek.Monday]
. Esto tiene la ventaja del hecho de que si el TDaysOfTheWeek
aumenta en tamaño, entonces no tengo que recordar el nuevo tamaño de la matriz, etc. ..... Sin embargo, volvamos al mundo de C #. Encontré este ejemplo C # Enum Array Example .
Podría hacer una clase o estructura que podría hacer el trabajo por usted
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]);
}
}
EDITAR
A falta de un lugar mejor para poner esto, estoy publicando una versión genérica de la clase Caster a continuación. Desafortunadamente, confía en los controles de tiempo de ejecución para aplicar TKey como una enumeración.
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]);
Si los valores de los elementos enum son contiguos, el método de matriz funciona bastante bien. Sin embargo, en cualquier caso, podría usar Dictionary<DayOfTheWeek, string>
(que es menos Dictionary<DayOfTheWeek, string>
, por cierto).
Si todo lo que necesita es esencialmente un mapa, pero no desea incurrir en gastos generales de rendimiento asociados con las búsquedas de diccionario, esto podría funcionar:
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();
}
}
Entonces en tu caso podrías derivar:
class DaysOfWeekToStringsMap:EnumIndexedArray<DayOfWeek,string>{};
Uso:
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);
}
Obviamente, esta implementación está respaldada por una matriz, por lo que las enumeraciones no contiguas como esta:
enum
{
Ok = 1,
NotOk = 1000000
}
daría como resultado un uso excesivo de la memoria.
Si necesita el máximo rendimiento posible, puede hacerlo menos genérico y perder todo el código genérico de manejo de enum que tuve que usar para que compilara y funcionara. Sin embargo, no hice un benchmark, así que tal vez no sea gran cosa.
La propiedad estática de almacenamiento en caché también podría ser útil.
Siempre puede hacer un mapeo adicional para obtener un índice de matriz de un valor enum de una manera consistente y definida:
int ArrayIndexFromDaysOfTheWeekEnum(DaysOfWeek day)
{
switch (day)
{
case DaysOfWeek.Sunday: return 0;
case DaysOfWeek.Monday: return 1;
...
default: throw ...;
}
}
Sé tan específico como puedas. Algún día alguien modificará tu enumeración y el código fallará porque el valor de la enumeración se usó (mal) como un índice de matriz.