2013-02-22 19 views

उत्तर

6

से अधिक समय तक मैं यह अधिकार प्राप्त करने के लिए आशा व्यक्त की लिया है, लेकिन यह काम करता है और परीक्षण किया गया है। उम्मीद है कि यह किसी को कुछ समय बचाता है!

private static readonly char[] FlagDelimiter = new [] { ',' }; 

    public static bool TryParseEnum<TEnum>(string value, out TEnum result) where TEnum : struct { 
     if (string.IsNullOrEmpty(value)) { 
      result = default(TEnum); 
      return false; 
     } 

     var enumType = typeof(TEnum); 

     if (!enumType.IsEnum) 
      throw new ArgumentException(string.Format("Type '{0}' is not an enum", enumType.FullName)); 


     result = default(TEnum); 

     // Try to parse the value directly 
     if (Enum.IsDefined(enumType, value)) { 
      result = (TEnum)Enum.Parse(enumType, value); 
      return true; 
     } 

     // Get some info on enum 
     var enumValues = Enum.GetValues(enumType); 
     if (enumValues.Length == 0) 
      return false; // probably can't happen as you cant define empty enum? 
     var enumTypeCode = Type.GetTypeCode(enumValues.GetValue(0).GetType()); 

     // Try to parse it as a flag 
     if (value.IndexOf(',') != -1) { 
      if (!Attribute.IsDefined(enumType, typeof(FlagsAttribute))) 
       return false; // value has flags but enum is not flags 

      // todo: cache this for efficiency 
      var enumInfo = new Dictionary<string, object>(); 
      var enumNames = Enum.GetNames(enumType); 
      for (var i = 0; i < enumNames.Length; i++) 
       enumInfo.Add(enumNames[i], enumValues.GetValue(i)); 

      ulong retVal = 0; 
      foreach(var name in value.Split(FlagDelimiter)) { 
       var trimmedName = name.Trim(); 
       if (!enumInfo.ContainsKey(trimmedName)) 
        return false; // Enum has no such flag 

       var enumValueObject = enumInfo[trimmedName]; 
       ulong enumValueLong; 
       switch (enumTypeCode) { 
        case TypeCode.Byte: 
         enumValueLong = (byte)enumValueObject; 
         break; 
        case TypeCode.SByte: 
         enumValueLong = (byte)((sbyte)enumValueObject); 
         break; 
        case TypeCode.Int16: 
         enumValueLong = (ushort)((short)enumValueObject); 
         break; 
        case TypeCode.Int32: 
         enumValueLong = (uint)((int)enumValueObject); 
         break; 
        case TypeCode.Int64: 
         enumValueLong = (ulong)((long)enumValueObject); 
         break; 
        case TypeCode.UInt16: 
         enumValueLong = (ushort)enumValueObject; 
         break; 
        case TypeCode.UInt32: 
         enumValueLong = (uint)enumValueObject; 
         break; 
        case TypeCode.UInt64: 
         enumValueLong = (ulong)enumValueObject; 
         break; 
        default: 
         return false; // should never happen 
       } 
       retVal |= enumValueLong; 
      } 
      result = (TEnum)Enum.ToObject(enumType, retVal); 
      return true; 
     } 

     // the value may be a number, so parse it directly 
     switch (enumTypeCode) { 
      case TypeCode.SByte: 
       sbyte sb; 
       if (!SByte.TryParse(value, out sb)) 
        return false; 
       result = (TEnum)Enum.ToObject(enumType, sb); 
       break; 
      case TypeCode.Byte: 
       byte b; 
       if (!Byte.TryParse(value, out b)) 
        return false; 
       result = (TEnum)Enum.ToObject(enumType, b); 
       break; 
      case TypeCode.Int16: 
       short i16; 
       if (!Int16.TryParse(value, out i16)) 
        return false; 
       result = (TEnum)Enum.ToObject(enumType, i16); 
       break; 
      case TypeCode.UInt16: 
       ushort u16; 
       if (!UInt16.TryParse(value, out u16)) 
        return false; 
       result = (TEnum)Enum.ToObject(enumType, u16); 
       break; 
      case TypeCode.Int32: 
       int i32; 
       if (!Int32.TryParse(value, out i32)) 
        return false; 
       result = (TEnum)Enum.ToObject(enumType, i32); 
       break; 
      case TypeCode.UInt32: 
       uint u32; 
       if (!UInt32.TryParse(value, out u32)) 
        return false; 
       result = (TEnum)Enum.ToObject(enumType, u32); 
       break; 
      case TypeCode.Int64: 
       long i64; 
       if (!Int64.TryParse(value, out i64)) 
        return false; 
       result = (TEnum)Enum.ToObject(enumType, i64); 
       break; 
      case TypeCode.UInt64: 
       ulong u64; 
       if (!UInt64.TryParse(value, out u64)) 
        return false; 
       result = (TEnum)Enum.ToObject(enumType, u64); 
       break; 
      default: 
       return false; // should never happen 
     } 

     return true; 
    } 
+0

आप उन स्विच कथन को प्रतिबिंब में बदल सकते हैं सभी प्रकारों को सूचीबद्ध करना और कोड को छोटा करना। –

+0

@ निकटर्नर: इस विधि के लिए प्रदर्शन महत्वपूर्ण है, प्रतिबिंब प्रदर्शन के लिए अच्छा नहीं है। –

+0

नोट: .net 4+ में 'Enum.Tryparse' इन अपवादों को फेंक नहीं देगा। https://msdn.microsoft.com/en-us/library/dd991317%28v=vs.110%29.aspx – Julian

3

यह Enum पर एक स्थिर विधि (स्थिर विस्तार तरीकों काफी मतलब नहीं है), लेकिन यह

public static class EnumHelpers 
{ 
    public static bool TryParse<TEnum>(string value, out TEnum result) 
     where TEnum : struct 
    { 
     try 
     { 
      result = (TEnum)Enum.Parse(typeof(TEnum), value); 
     } 
     catch 
     { 
      return false; 
     } 

     return true; 
    } 
} 
14

मैं एक try-catch का उपयोग कर नापसंद किसी भी रूपांतरण विफलताओं को संभालने के लिए काम करना चाहिए नहीं होगा या अन्य गैर असाधारण घटनाओं अपने आवेदन के सामान्य प्रवाह के भाग के रूप, इसलिए .NET 3.5 के लिए अपने खुद के Enum.TryParse विधि और पहले Enum.IsDefined() पद्धति के उपयोग सुनिश्चित करने के लिए बनाता है वहाँ एक अपवाद Enum.Parse द्वारा फेंका नहीं किया जाएगा() । तुम भी अगर मान शून्य है एक ArgumentNullException को रोकने के लिए value पर कुछ अशक्त चेकों शामिल कर सकते हैं।

public static bool TryParse<TEnum>(string value, out TEnum result) 
    where TEnum : struct, IConvertible 
{ 
    var retValue = value == null ? 
       false : 
       Enum.IsDefined(typeof(TEnum), value); 
    result = retValue ? 
       (TEnum)Enum.Parse(typeof(TEnum), value) : 
       default(TEnum); 
    return retValue; 
} 

जाहिर है इस विधि Enum वर्ग में रहते नहीं होगा तो आप शामिल करने के लिए है कि इस उपयुक्त होगा एक वर्ग की आवश्यकता होगी।

एक सीमा सामान्य तरीकों पर enum बाधा की कमी है, इसलिए आपको यह विचार करना होगा कि आप गलत प्रकारों को कैसे संभालना चाहते हैं। Enum.IsDefined फेंक होगा एक ArgumentException अगर TEnum एक enum नहीं है, लेकिन केवल एक अन्य विकल्प के लिए एक क्रम की जांच और एक अलग अपवाद फेंक है, इसलिए मैं आम तौर पर एक अतिरिक्त जांच में शामिल न करें और बस प्रकार इन तरीकों में जाँच संभाल मेरे लिए करते हैं। मैं IConvertible को एक और बाधा के रूप में जोड़ने पर विचार करता हूं, बस इस प्रकार को और भी बाधित करने में मदद करता हूं।

+0

मैं से यह एक बेहतर पसंद है मेरी निश्चित रूप से दृष्टिकोण (अब हटा दिया गया)। –

+0

+1 सहमत हैं, अगर मैं एनम विधियों को देखकर एक और मिनट बिताता और आईएसडीफिन विधि को देखता तो यह सबसे अधिक संभावना होती कि क्या होता। :) –

+1

आपके उत्तर के लिए धन्यवाद। हालांकि यह एक अच्छी शुरुआत थी, इसे .NET 4 के कार्यान्वयन के समान बनाने के लिए कुछ अन्य विचार हैं (उदाहरण के लिए अल्पविराम से अलग ध्वज, मूल्य के रूप में मूल्य, संख्या संख्यात्मक संख्या) –

1

NLog हम भी नेट 3.5 के लिए Enum.TryParse की जरूरत है। हमने इस पोस्ट से प्रभावित बुनियादी सुविधाओं (केवल पार्स, केस संवेदनशील और असंवेदनशील, कोई झंडे) लागू नहीं किया है।

यह बुनियादी कार्यान्वयन अत्यधिक इकाई का परीक्षण किया तो यह Microsoft`s नेट 4 कार्यान्वयन के रूप में ही व्यवहार किया जाता है।

कोड the NLog GitHub में पाया जा सकता है, और भी unit tests are on GitHub (XUnit)

प्रयोग (सभी नेट संस्करण) - एक ही हस्ताक्षर के रूप में नेट 4,0

EnumHelpers.TryParse(value, true, out parsedValue) //case insensitive 
//or 
EnumHelpers.TryParse(value, out parsedValue)