Enum मान के लिए TryParse कैसे करें?


94

मैं एक फ़ंक्शन लिखना चाहता हूं जो किसी दिए गए मान (स्ट्रिंग के रूप में पारित) को मान्य कर सकता है enum। एक मैच के मामले में, इसे एनम उदाहरण को वापस करना चाहिए; अन्यथा, उसे एक डिफ़ॉल्ट मान वापस करना चाहिए।

फ़ंक्शन आंतरिक रूप से try/ catchका उपयोग नहीं कर सकता है Enum.Parse, जो कि उपयोग को बाहर करता है , जो एक अमान्य तर्क दिए जाने पर एक अपवाद फेंकता है।

मैं इसे TryParseलागू करने के लिए किसी फ़ंक्शन की तर्ज पर कुछ का उपयोग करना चाहूंगा :

public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
   object enumValue;
   if (!TryParse (typeof (TEnum), strEnumValue, out enumValue))
   {
       return defaultValue;
   }
   return (TEnum) enumValue;
}

8
मैं इस प्रश्न को नहीं समझता; आप कह रहे हैं "मैं इस समस्या को हल करना चाहता हूं, लेकिन मैं किसी भी तरीके का उपयोग नहीं करना चाहता हूं जो मुझे एक समाधान देगा।" क्या बात है?
डोमोनिक जूल 4'09

1
समाधान के लिए प्रयास करना / पकड़ना क्या है? यदि आप अपवाद से बचने की कोशिश कर रहे हैं तो वे 'महंगा' हैं, कृपया अपने आप को एक विराम दें। आपके मुख्य कोड की तुलना में 99% मामलों में, लागत को फेंकने / पकड़ने के लिए लागत अपवाद नगण्य है।
SolutionYogi

1
अपवाद हैंडलिंग की लागत इतनी बुरी नहीं है। नरक, इस सभी गणना रूपांतरण की आंतरिक कार्यान्वयन अपवाद हैंडलिंग से भरा है। मैं वास्तव में अपवादों को नापसंद करता हूं और सामान्य अनुप्रयोग तर्क के दौरान पकड़ा जाता हूं। यह कभी-कभी सभी अपवादों को तोड़ने पर उपयोगी हो सकता है (यहां तक ​​कि जब वे पकड़े जाते हैं)। सभी जगह अपवादों को फेंकने से बहुत अधिक कष्टप्रद उपयोग होगा :)
थोरिन

3
@ डोमिनिक: मैं सिर्फ एक बेहतर समाधान की तलाश में हूं जो मुझे पहले से पता है। क्या आप कभी भी एक रेलवे पूछताछ के लिए एक मार्ग या ट्रेन के लिए पूछेंगे जिसे आप पहले से जानते हैं :)।
मनीष बसंतानी

2
@ एम्बी, एक कोशिश / कैच ब्लॉक में प्रवेश करने की लागत नगण्य है। एक अपवाद की लागत नहीं है, लेकिन फिर यह असाधारण होना चाहिए, नहीं? यह भी मत कहो, "हम कभी नहीं जानते" ... कोड को प्रोफाइल करें और पता करें। अगर कुछ धीमा है, तो अपना समय बर्बाद न करें!
अकमद

जवाबों:


31

जैसा कि दूसरों ने कहा है, आपको अपना खुद का कार्यान्वयन करना होगा TryParse। साइमन मूरियर एक पूर्ण कार्यान्वयन प्रदान कर रहा है जो हर चीज का ख्याल रखता है।

यदि आप बिटफील्ड एनम (यानी झंडे) का उपयोग कर रहे हैं, तो आपको एक स्ट्रिंग भी संभालनी होगी, "MyEnum.Val1|MyEnum.Val2"जो दो एनम वैल्यू का संयोजन है। यदि आप सिर्फ Enum.IsDefinedइस तार के साथ कॉल करते हैं, तो यह गलत तरीके से वापस आ जाएगा, भले ही Enum.Parseइसे सही ढंग से संभालता हो।

अपडेट करें

जैसा कि टिप्पणियों में लिसा और ईसाई ने उल्लेख किया है, Enum.TryParseअब .NET # और ऊपर C # के लिए उपलब्ध है।

MSDN डॉक्स


शायद कम से कम सेक्सी, लेकिन मैं मानता हूं कि यह निश्चित रूप से सबसे अच्छा है जब तक कि आपका कोड .NET 4 में माइग्रेट न हो जाए
लिसा

1
जैसा कि नीचे उल्लेख किया गया है, लेकिन वास्तव में दिखाई नहीं देता है: जैसा कि .Net 4 Enum.TryParse उपलब्ध है और बिना अतिरिक्त कोडिंग के काम करता है। अधिक जानकारी MSDN से उपलब्ध है: msdn.microsoft.com/library/vstudio/dd991317%28v=vs.100%29.aspx
ईसाई

106

Enum.IsDefined चीजें हो जाएंगी। यह एक TryParse के रूप में के रूप में कुशल नहीं हो सकता है, लेकिन यह अपवाद को संभालने के बिना काम करेंगे।

public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
    if (!Enum.IsDefined(typeof(TEnum), strEnumValue))
        return defaultValue;

    return (TEnum)Enum.Parse(typeof(TEnum), strEnumValue);
}

वर्थ नोटिंग: एक TryParseविधि .NET 4.0 में जोड़ी गई थी।


1
सबसे अच्छा जवाब मैंने अब तक देखा है ... कोई कोशिश नहीं / पकड़, नहीं GetNames :)
थॉमस लेवेस्क


6
इसके अलावा IsDefined
एंथनी जॉन्सटन

2
@Anthony: यदि आप केस असंवेदनशीलता का समर्थन करना चाहते हैं, तो आपको आवश्यकता होगी GetNames। आंतरिक रूप से, इन सभी विधियों (सहित Parse) का उपयोग GetHashEntryकरता है, जो वास्तविक प्रतिबिंब करता है - एक बार। चमकदार पक्ष पर, .NET 4.0 में एक TryParse है, और यह सामान्य भी है :)
Thorarin

+1 यह मेरा दिन बच गया! मैं .NET 4 से .NET 3.5 के कोड का एक गुच्छा वापस कर रहा हूं और आपने मुझे बचा लिया :)
daitangio

20

यहाँ एक कस्टम कार्यान्वयन है EnumTryParse। अन्य सामान्य कार्यान्वयनों के विपरीत, यह Flagsविशेषता के साथ चिह्नित एनम का भी समर्थन करता है ।

    /// <summary>
    /// Converts the string representation of an enum to its Enum equivalent value. A return value indicates whether the operation succeeded.
    /// This method does not rely on Enum.Parse and therefore will never raise any first or second chance exception.
    /// </summary>
    /// <param name="type">The enum target type. May not be null.</param>
    /// <param name="input">The input text. May be null.</param>
    /// <param name="value">When this method returns, contains Enum equivalent value to the enum contained in input, if the conversion succeeded.</param>
    /// <returns>
    /// true if s was converted successfully; otherwise, false.
    /// </returns>
    public static bool EnumTryParse(Type type, string input, out object value)
    {
        if (type == null)
            throw new ArgumentNullException("type");

        if (!type.IsEnum)
            throw new ArgumentException(null, "type");

        if (input == null)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        input = input.Trim();
        if (input.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        string[] names = Enum.GetNames(type);
        if (names.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        Type underlyingType = Enum.GetUnderlyingType(type);
        Array values = Enum.GetValues(type);
        // some enums like System.CodeDom.MemberAttributes *are* flags but are not declared with Flags...
        if ((!type.IsDefined(typeof(FlagsAttribute), true)) && (input.IndexOfAny(_enumSeperators) < 0))
            return EnumToObject(type, underlyingType, names, values, input, out value);

        // multi value enum
        string[] tokens = input.Split(_enumSeperators, StringSplitOptions.RemoveEmptyEntries);
        if (tokens.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        ulong ul = 0;
        foreach (string tok in tokens)
        {
            string token = tok.Trim(); // NOTE: we don't consider empty tokens as errors
            if (token.Length == 0)
                continue;

            object tokenValue;
            if (!EnumToObject(type, underlyingType, names, values, token, out tokenValue))
            {
                value = Activator.CreateInstance(type);
                return false;
            }

            ulong tokenUl;
            switch (Convert.GetTypeCode(tokenValue))
            {
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.SByte:
                    tokenUl = (ulong)Convert.ToInt64(tokenValue, CultureInfo.InvariantCulture);
                    break;

                //case TypeCode.Byte:
                //case TypeCode.UInt16:
                //case TypeCode.UInt32:
                //case TypeCode.UInt64:
                default:
                    tokenUl = Convert.ToUInt64(tokenValue, CultureInfo.InvariantCulture);
                    break;
            }

            ul |= tokenUl;
        }
        value = Enum.ToObject(type, ul);
        return true;
    }

    private static char[] _enumSeperators = new char[] { ',', ';', '+', '|', ' ' };

    private static object EnumToObject(Type underlyingType, string input)
    {
        if (underlyingType == typeof(int))
        {
            int s;
            if (int.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(uint))
        {
            uint s;
            if (uint.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(ulong))
        {
            ulong s;
            if (ulong.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(long))
        {
            long s;
            if (long.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(short))
        {
            short s;
            if (short.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(ushort))
        {
            ushort s;
            if (ushort.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(byte))
        {
            byte s;
            if (byte.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(sbyte))
        {
            sbyte s;
            if (sbyte.TryParse(input, out s))
                return s;
        }

        return null;
    }

    private static bool EnumToObject(Type type, Type underlyingType, string[] names, Array values, string input, out object value)
    {
        for (int i = 0; i < names.Length; i++)
        {
            if (string.Compare(names[i], input, StringComparison.OrdinalIgnoreCase) == 0)
            {
                value = values.GetValue(i);
                return true;
            }
        }

        if ((char.IsDigit(input[0]) || (input[0] == '-')) || (input[0] == '+'))
        {
            object obj = EnumToObject(underlyingType, input);
            if (obj == null)
            {
                value = Activator.CreateInstance(type);
                return false;
            }
            value = obj;
            return true;
        }

        value = Activator.CreateInstance(type);
        return false;
    }

1
आपने सबसे अच्छा कार्यान्वयन प्रदान किया और मैंने इसे अपने उद्देश्यों के लिए उपयोग किया है; हालाँकि, मैं सोच रहा हूँ कि आप Activator.CreateInstance(type)डिफ़ॉल्ट एनम मान बनाने के लिए क्यों उपयोग करते हैं और नहीं Enum.ToObject(type, 0)। बस स्वाद की बात है?
पियरे अरनौद

1
@ पियरे - हम्म ... नहीं, यह उस समय और अधिक स्वाभाविक लग रहा था :-) शायद Enum.ToObject तेज है क्योंकि यह आंतरिक रूप से एक आंतरिक कॉल InternalBoxEnum का उपयोग कर रहा है? मैंने कभी यह जाँच नहीं की ...
साइमन मूरियर

2
जैसा कि नीचे उल्लेख किया गया है, लेकिन वास्तव में दिखाई नहीं देता है: जैसा कि .Net 4 Enum.TryParse उपलब्ध है और बिना अतिरिक्त कोडिंग के काम करता है। अधिक जानकारी MSDN से उपलब्ध है: msdn.microsoft.com/library/vstudio/dd991317%28v=vs.100%29.aspx
ईसाई

16

अंत में आपको इसे लागू करना होगा Enum.GetNames:

public bool TryParseEnum<T>(string str, bool caseSensitive, out T value) where T : struct {
    // Can't make this a type constraint...
    if (!typeof(T).IsEnum) {
        throw new ArgumentException("Type parameter must be an enum");
    }
    var names = Enum.GetNames(typeof(T));
    value = (Enum.GetValues(typeof(T)) as T[])[0];  // For want of a better default
    foreach (var name in names) {
        if (String.Equals(name, str, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)) {
            value = (T)Enum.Parse(typeof(T), name);
            return true;
        }
    }
    return false;
}

अतिरिक्त नोट्स:

  • Enum.TryParse.NET 4 में शामिल है। यहाँ देखें http://msdn.microsoft.com/library/dd991876(VS.100).aspx
  • एक और तरीका यह होगा कि Enum.Parseजब वह फेल हो जाए तो अपवाद को सीधे पकड़ ले। यह तेजी से हो सकता है जब एक मैच पाया जाता है, लेकिन अगर नहीं तो धीमा होने की संभावना है। आपके द्वारा संसाधित किए जा रहे डेटा के आधार पर यह शुद्ध सुधार हो सकता है या नहीं भी हो सकता है।

EDIT: बस इस पर एक बेहतर कार्यान्वयन देखा गया है, जो आवश्यक जानकारी को कैश करता है: http://damieng.com/blog/2010/10/17/enums-better-syntax-improved-and-tryparse-in-net-net 3-5


मैं डिफ़ॉल्ट मान सेट करने के लिए डिफ़ॉल्ट (T) का उपयोग करने का सुझाव देने वाला था। यह पता चलता है कि यह सभी एनमों के लिए काम नहीं करेगा। उदाहरण के लिए, यदि एनम के लिए अंतर्निहित प्रकार डिफ़ॉल्ट था (टी) हमेशा 0 लौटेगा, जो एनम के लिए मान्य हो सकता है या नहीं।
डैनियल बॉलिंगर

Damieng के ब्लॉग पर कार्यान्वयन विशेषता के साथ enums का समर्थन नहीं करता है Flags
उवे कीम

9

.NET 4.5 पर आधारित है

नीचे नमूना कोड

using System;

enum Importance
{
    None,
    Low,
    Medium,
    Critical
}

class Program
{
    static void Main()
    {
    // The input value.
    string value = "Medium";

    // An unitialized variable.
    Importance importance;

    // Call Enum.TryParse method.
    if (Enum.TryParse(value, out importance))
    {
        // We now have an enum type.
        Console.WriteLine(importance == Importance.Medium);
    }
    }
}

संदर्भ: http://www.dotnetperls.com/enum-parse


4

मेरे पास एक अनुकूलित कार्यान्वयन है जो आप UnconstrainedMelody में उपयोग कर सकते हैं । प्रभावी रूप से यह सिर्फ नामों की सूची को कैशिंग कर रहा है, लेकिन यह एक अच्छा, दृढ़ता से टाइप किया गया, उदारतापूर्वक विवश तरीके से कर रहा है :)


4
enum EnumStatus
{
    NAO_INFORMADO = 0,
    ENCONTRADO = 1,
    BLOQUEADA_PELO_ENTREGADOR = 2,
    DISPOSITIVO_DESABILITADO = 3,
    ERRO_INTERNO = 4,
    AGARDANDO = 5
}

...

if (Enum.TryParse<EnumStatus>(item.status, out status)) {

}

2

वर्तमान में Enum.TryParse बॉक्स से बाहर नहीं है। यह कनेक्ट ( अभी भी कोई Enum.TryParse ) पर अनुरोध नहीं किया गया है और इसे .NET 3.5 के साथ अगले ढांचे में संभावित समावेश का संकेत मिला है। अब आपको सुझाए गए वर्कअराउंड को लागू करना होगा।


1

अपवाद से बचने का एकमात्र तरीका GetNames () विधि का उपयोग करना है, और हम सभी जानते हैं कि अपवादों को एप्लिकेशन एप्लिकेशन लॉजिक के लिए दुरुपयोग नहीं किया जाना चाहिए :)


1
यह एकमात्र तरीका नहीं है। Enum.IsDefined (..) उपयोगकर्ता कोड में डाले जा रहे अपवादों को रोक देगा।
थोरिन

1

क्या एक गतिशील रूप से उत्पन्न फ़ंक्शन / डिक्शनरी को अनुमति देना कैशिंग है?

क्योंकि आप समय से पहले एनम के प्रकार को नहीं जानते (प्रकट करते हैं), पहले निष्पादन कुछ बाद के निष्पादन उत्पन्न कर सकता था जिसका फायदा उठा सकता था।

आप Enum.GetNames () का परिणाम भी कैश कर सकते हैं

क्या आप CPU या मेमोरी के लिए ऑप्टिमाइज़ करने की कोशिश कर रहे हैं? क्या आपको वास्तव में जरूरत है?


CPU को Idea करना है। सहमत हूं कि मैं इसे लागत स्मृति पर कर सकता हूं। लेकिन इसका समाधान मुझे नहीं लग रहा है। धन्यवाद।
मनीष बसंतानी

0

जैसा कि दूसरों ने पहले ही कहा था, यदि आप ट्राय एंड कैच का उपयोग नहीं करते हैं, तो आपको IsDefined या GetNames का उपयोग करने की आवश्यकता है ... यहां कुछ नमूने दिए गए हैं ... वे मूल रूप से सभी समान हैं, पहले एक nullable enums को हैंडल करते हैं। मैं 2 को पसंद करता हूं क्योंकि यह स्ट्रिंग्स पर एक विस्तार है, न कि एनम ... लेकिन आप जैसा चाहें उन्हें मिला सकते हैं!

  • www.objectreference.net/post/Enum-TryParse-Extension-Method.aspx
  • flatlinerdoa.spaces.live.com/blog/cns!17124D03A9A052B0!605.entry
  • mironabramson.com/blog/post/2008/03/Another-version-for-the-missing-method-EnumTryParse.aspx
  • lazyloading.blogspot.com/2008/04/enumtryparse-with-net-35-extension.html

0

वहाँ एक TryParse नहीं है क्योंकि Enum का प्रकार रनटाइम तक ज्ञात नहीं है। एक TryParse, जो उसी पद्धति का अनुसरण करता है, जैसा कि Date.TryParse विधि कहती है, ByRef पैरामीटर पर एक अंतर्निहित रूपांतरण त्रुटि फेंकती है।

मैं कुछ इस तरह से करने का सुझाव देता हूं:

//1 line call to get value
MyEnums enumValue = (Sections)EnumValue(typeof(Sections), myEnumTextValue, MyEnums.SomeEnumDefault);

//Put this somewhere where you can reuse
public static object EnumValue(System.Type enumType, string value, object NotDefinedReplacement)
{
    if (Enum.IsDefined(enumType, value)) {
        return Enum.Parse(enumType, value);
    } else {
        return Enum.Parse(enumType, NotDefinedReplacement);
    }
}

उन Tryतरीकों के लिए जिनके परिणाम मान प्रकार हो सकते हैं, या जहां nullवैध परिणाम हो सकते हैं (उदाहरण के Dictionary.TryGetValue, which has both such traits), the normal pattern is for a लिए bool, वापस जाने के लिए ' प्रयास' विधि और एक outपैरामीटर के रूप में परिणाम पास करें । उन लोगों के लिए जो वर्ग प्रकार लौटाते हैं जहां nullएक वैध परिणाम नहीं है, nullवापसी का उपयोग करने में कोई कठिनाई नहीं है। विफलता को इंगित करने के लिए।
सुपरकाट

-1

Enum class (संरचना?) पर एक नज़र डालें। उस पर एक पार्स विधि है, लेकिन मैं एक tryparse के बारे में निश्चित नहीं हूं।


मैं Enum.Parse (टाइपोफ (TEnum), strEnumValue) विधि के बारे में जानता हूं। यह ArgumentException को फेंकता है यदि strEnumValue मान्य नहीं है। TryParse की तलाश ........
मनीष बसंतानी

-2

यह विधि एक प्रकार की एनम को रूपांतरित करेगी:

  public static TEnum ToEnum<TEnum>(object EnumValue, TEnum defaultValue)
    {
        if (!Enum.IsDefined(typeof(TEnum), EnumValue))
        {
            Type enumType = Enum.GetUnderlyingType(typeof(TEnum));
            if ( EnumValue.GetType() == enumType )
            {
                string name = Enum.GetName(typeof(HLink.ViewModels.ClaimHeaderViewModel.ClaimStatus), EnumValue);
                if( name != null)
                    return (TEnum)Enum.Parse(typeof(TEnum), name);
                return defaultValue;
            }
        }
        return (TEnum)Enum.Parse(typeof(TEnum), EnumValue.ToString());
    } 

यह अंतर्निहित प्रकार की जांच करता है और इसे पार्स करने के लिए नाम प्राप्त करता है। यदि सब कुछ विफल रहता है तो यह डिफ़ॉल्ट मान लौटाएगा।


3
यह क्या कर रहा है "Enum.GetName (टाइपोफ़ (HLink.ViewModels.ClaimHeaderViewModel.ClaimStatus), EnumValue)" संभवतः आपके स्थानीय कोड पर कुछ निर्भरता है।
मनीष बसंती
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.