2009-06-11 18 views
23

मैं this question में के रूप में ही करना चाहते हैं के रूप में एक enum का उपयोग करना, वह यह है कि:सी # में एक सरणी सूचकांक

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

... 

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

हालांकि, मैं नहीं बल्कि कुछ अभिन्न इसलिए, बजाय इस त्रुटि प्रवण कोड लिखने के लिए होता है । क्या सी # में मॉड्यूल में बनाया गया है जो यह करता है?

+3

टिनी आपके नाम "DaysOfTheWeek" के बारे में टिप्पणी है गैर-झंडे-शैली के enums में एकवचन नाम होना चाहिए और झंडे-शैली enums बहुवचन नाम होना चाहिए, तो "DayOfTheWeek" बेहतर होगा। http://msdn.microsoft.com/en-us/library/ms229040.aspx – RenniePet

उत्तर

15

यदि आपके enum आइटम के मूल्य विरोधाभासी हैं, तो सरणी विधि बहुत अच्छी तरह से काम करती है। हालांकि, किसी भी मामले में, आप Dictionary<DayOfTheWeek, string> (जो कि कम प्रदर्शन करने वाला है) का उपयोग कर सकते हैं।

+0

प्रदर्शन पर एक महत्वपूर्ण प्रभाव पड़ता है यही कारण है कि? कैसे? –

+1

@Spencer: शब्दकोश लुकअप प्रत्यक्ष सरणी अनुक्रमणिका (या सूची अनुक्रमणिका) से बहुत धीमी है। यदि आप इसे बहुत कुछ कर रहे हैं, तो यह perf पर एक उल्लेखनीय प्रभाव हो सकता है। –

+0

हाँ, कि समाधान मैं mousebutton के लिए चुना है, के रूप में यह एक झंडा enum (उर्फ 0001 ठीक है, 0010 मध्य, 0100 आदि छोड़कर चला गया) है। फिर भी, इस तरह की एक साधारण चीज़ के लिए बहुत बदसूरत। – Nefzen

7

आप एक वर्ग या struct है कि आप


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

संपादित

एक बेहतर जगह यह डाल करने के लिए की कमी के लिए, मैं एक सामान्य पोस्टिंग कर रहा हूँ के लिए काम करते कर सकता है कर सकता है नीचे कास्टर वर्ग का संस्करण। दुर्भाग्यवश, यह टीके को एक 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

+1, इस, कम से कम, कुछ इसे करने के लिए नहीं बनाया गया था (और करने के लिए नहीं किया जाना चाहिए) कर कर रही है में एक enum जूता पहनने का साधन करने की कोशिश कर के दर्द को समाहित। –

+0

ठीक है, मैं सहमत हूं कि सेटर्स के लिए कुछ और सैन किया जाना चाहिए। लेकिन मैं इसे कभी ऐसा नहीं करूँगा जिसे "इस्तेमाल नहीं किया जाना चाहिए।" एक और विकल्प कस्टम रीडोनली structs बनाने के लिए होगा जो एक समान फैशन में उपयोग किया जाता है। –

+0

@Matthew चूना फिरी हुई, मैं पर प्रतिक्रिया जब मैं कहता हूँ शायद हूँ "नहीं किया जाना चाहिए," लेकिन मैं व्यक्तिगत रूप से कभी नहीं देखा है, और कल्पना नहीं कर सकते, एक enum बनाने के लिए एक अच्छा न्यायोचित कारण ही में एक इंडेक्सर के रूप में काम करने के लिए एक सरणी। मैं आत्मविश्वास से कह सकता हूं कि, इस सीमित मामले में, आप एक enum के सभी लाभ खो देते हैं, और बदले में कुछ भी हासिल नहीं करते हैं। –

4

ये रहा:

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

:

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

तुम सच में लंबाई की जरूरत है, तो बस .Length परिणाम :) आप के साथ मान प्राप्त कर सकते पर ले आप एक सतत और परिभाषित तरीके से enum मान की सरणी अनुक्रमणिका प्राप्त करने के लिए हमेशा कुछ अतिरिक्त मैपिंग कर सकते हैं:

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

जितना हो सके उतना सटीक बनें। एक दिन कोई आपके enum को संशोधित करेगा और कोड विफल हो जाएगा क्योंकि enum का मान सरणी अनुक्रमणिका के रूप में उपयोग किया गया था (गलत)।

+1

इस मामले में यह माँ होगा एन एन परिभाषा में मूल्यों को निर्दिष्ट करने के लिए बस समझ में आता है। – van

+0

@van, आप इस मामले के बारे में सही हैं, लेकिन @ डेविड हंपोहल के इस दावे पर कुछ योग्यता है कि कोड अंततः विफल हो सकता है। DaysOfWeek के मामले में, संभावना कम है, लेकिन व्यापार मूल्यों के आधार पर enums अंतर्निहित मूल्यों को स्थानांतरित करने के कारण बदल सकता है। –

2

इंडेक्स के रूप में प्रयुक्त एनम का कॉम्पैक्ट फॉर्म और किसी भी प्रकार के शब्दकोश को असाइन करना और दृढ़ता से टाइप करना। इस मामले में नाव मूल्यों लौटा दिए जाते हैं लेकिन मूल्यों जटिल कक्षा गुण और तरीकों और होने उदाहरणों हो सकता है और अधिक:

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

तो आप सभी की जरूरत अनिवार्य रूप से एक नक्शा है, लेकिन प्रदर्शन शब्दकोश लुकअप के साथ जुड़े भूमि के ऊपर उठाना नहीं करना चाहते , इस काम हो सकता है:

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

जाहिर है इस कार्यान्वयन एक सरणी के द्वारा समर्थित है, इस तरह तो गैर-निरंतर enums:

enum 
{ 
    Ok = 1, 
    NotOk = 1000000 
} 

अत्यधिक स्मृति के उपयोग में परिणाम होगा।

यदि आपको अधिकतम संभव प्रदर्शन की आवश्यकता है तो आप इसे कम सामान्य और ढीला कर सकते हैं जो सभी जेनेरिक एनम हैंडलिंग कोड को संकलित करने और काम करने के लिए उपयोग करना था। हालांकि मैंने इसे बेंचमार्क नहीं किया था, इसलिए शायद यह कोई बड़ा सौदा नहीं है।

कुंजी स्थिर संपत्ति कैशिंग भी मदद कर सकता है।

0

भविष्य में संदर्भ के ऊपर समस्या के रूप में संक्षेप किया जा सकता है इस प्रकार है:

मैं डेल्फी से आते हैं इस प्रकार है जहां एक सरणी को परिभाषित कर सकते हैं:

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

    TDaysOfTheWeekStrings = array[TDaysOfTheWeek); 

तो फिर तुम सरणी मिन और का उपयोग कर के माध्यम से पुनरावृति कर सकते हैं मैक्स:

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

इस हालांकि काफी काल्पनिक उदाहरण है, जब आप कोड में बाद में सरणी पदों के साथ काम कर रहे हैं मैं सिर्फ टाइप कर सकते हैं DaysOfTheWeekStrings[TDaysOfTheWeek.Monday] । यह तथ्य यह है कि मैं आकार में TDaysOfTheWeek वृद्धि तो मैं सरणी आदि के नए आकार को याद है ..... लेकिन वापस सी # दुनिया की जरूरत नहीं है चाहिए का लाभ दिया है। मुझे यह उदाहरण C# Enum Array Example मिला है।

0

मुझे एहसास है कि यह एक पुराना सवाल है, लेकिन इस तथ्य के बारे में कई टिप्पणियां हुई हैं कि अब तक सभी समाधानों को रन-टाइम चेक है ताकि यह सुनिश्चित किया जा सके कि डेटा प्रकार एक enum है। सी # मानक का कहना है कि: यहाँ संकलन समय चेकों के साथ एक समाधान के लिए एक पूर्ण समाधान (कुछ उदाहरण के साथ) (और साथ ही अपने साथी डेवलपर्स से कुछ टिप्पणियाँ और विचार विमर्श)

//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()) ; 
     } 
    } 
} 
संबंधित मुद्दे