एक Enum के लिए टी विवश जेनरिक विधि बनाएँ


1187

मैं Enum.Parseअवधारणा का विस्तार करने के लिए एक फ़ंक्शन का निर्माण कर रहा हूं

  • यदि कोई Enum मान नहीं मिला है, तो डिफ़ॉल्ट मान पार्स किया जा सकता है
  • क्या मामला असंवेदनशील है

इसलिए मैंने निम्नलिखित लिखा:

public static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
{
    if (string.IsNullOrEmpty(value)) return defaultValue;
    foreach (T item in Enum.GetValues(typeof(T)))
    {
        if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
    }
    return defaultValue;
}

मुझे एक त्रुटि हो रही है बाधा विशेष वर्ग नहीं हो सकती है System.Enum

पर्याप्त रूप से उचित है, लेकिन जेनेरिक एनम की अनुमति देने के लिए एक वर्कअराउंड है, या क्या मुझे Parseफ़ंक्शन की नकल करनी है और एक विशेषता के रूप में एक प्रकार पास करना है, जो आपके कोड के लिए बदसूरत मुक्केबाजी की आवश्यकता को बल देता है।

संपादित करें नीचे दिए गए सभी सुझावों की बहुत सराहना की गई है, धन्यवाद।

पर बसे हैं (केस असंवेदनशीलता बनाए रखने के लिए मैंने लूप छोड़ दिया है - एक्सएमएल को पार्स करते समय मैं इसका उपयोग कर रहा हूं)

public static class EnumUtils
{
    public static T ParseEnum<T>(string value, T defaultValue) where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");
        if (string.IsNullOrEmpty(value)) return defaultValue;

        foreach (T item in Enum.GetValues(typeof(T)))
        {
            if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        }
        return defaultValue;
    }
}

EDIT: (16 फरवरी 2015) जूलियन लेबोस्क्वैन ने हाल ही में एमएसआईएल या एफ # के नीचे एक कंपाइलर लागू टाइप-सेफ जेनेरिक सॉल्यूशन पोस्ट किया है , जो देखने लायक है, और एक अपवोट है। मैं इस एडिट को हटा दूंगा यदि सॉल्यूशन पेज को आगे बढ़ाता है।


10
शायद आपको ToLower () के बजाय ToUpperInvariant () का उपयोग करना चाहिए ...
Max Galkin

31
@ शमी: जैसे ही आप एक्सटेंशन विधि के लिए एक मान प्रकार पास करते हैं, आप इसकी एक प्रति पर काम कर रहे होते हैं, इसलिए आप अपनी स्थिति नहीं बदल सकते।
गारो येरिज़ेरियन

4
यह जान लें कि यह एक पुराना धागा है, यह नहीं जानते कि क्या उन्होंने चीजें बदली हैं, लेकिन विस्तार के तरीके मूल्य प्रकारों के लिए ठीक काम करते हैं, सुनिश्चित करें कि वे हमेशा उतना अर्थ नहीं दे सकते हैं, लेकिन मैंने "सार्वजनिक स्थैतिक टाइमस्पैन सेकंड्स (इस int x) {का उपयोग किया है "Wait.For (5.Seconds ()) ..." के सिंटैक्स को सक्षम करने के लिए TimeSpan.FromSeconds (x);} "पर लौटें।
Jens

6
एहसास यह सवाल का हिस्सा नहीं था, लेकिन आप StringComparison.InvariantCultureIgnoreCase
Firestrand

जवाबों:


1005

चूंकि इंटरफ़ेस Enumलागू होता IConvertibleहै, इसलिए बेहतर कार्यान्वयन कुछ इस तरह से होना चाहिए:

public T GetEnumFromString<T>(string value) where T : struct, IConvertible
{
   if (!typeof(T).IsEnum) 
   {
      throw new ArgumentException("T must be an enumerated type");
   }

   //...
}

यह अभी भी लागू होने वाले मूल्य प्रकारों को पारित करने की अनुमति देगा IConvertible। संभावना हालांकि दुर्लभ हैं।


2
.NET 2.0 से जेनरिक उपलब्ध हैं। इसलिए वे vb 2005 में भी उपलब्ध हैं।
विवेक

46
ठीक है, इसे और भी विवश करें, यदि आप इस रास्ते से नीचे जाना चाहते हैं ... "क्लास टेस्टक्लास <टी> जहां टी: स्ट्रक्चर, IComparable, IFormattable, IConvertible" का उपयोग करें
Ricardo Nolde

106
एक अन्य सुझाव पहचानकर्ता टीएनम के साथ सामान्य प्रकार को परिभाषित करना है। इस प्रकार: सार्वजनिक TEnum GetEnumFromString <TEnum> (स्ट्रिंग मूल्य) जहां TEnum: संरचना, IConvertible, IComparible, IFormattable {}
लीसा

11
आप अन्य इंटरफेस को शामिल करके बहुत अधिक लाभ नहीं उठाते हैं क्योंकि लगभग सभी अंतर्निहित मूल्य प्रकार उन सभी इंटरफेस को लागू करते हैं। यह एक जेनेरिक एक्सटेंशन पद्धति पर बाधाओं के लिए विशेष रूप से सच है, जो इस तथ्य को छोड़कर, एनमों पर काम करने के लिए बेहद आसान है, उन विस्तार विधियों को एक वायरस की तरह है जो आपकी सभी वस्तुओं को संक्रमित करता है। IConvertable कम से कम इसे काफी नीचे गिरा देता है।
रुसबिशप

2
@ समिअम: जब आपने पोस्ट किया था, तो यह धागा क्या था, साढ़े 6 साल का था, और आप सही थे, किसी भी उत्तर में कोई संकलन-समय की जाँच नहीं। फिर केवल 3 दिन बाद, 6 साल बाद, आपको अपनी इच्छा मिली - नीचे जूलियन लेबोस्क्वाइन का पोस्ट तरीका देखें।
डेविड आई। मैकिंटोश

662

यह सुविधा अंत में C # 7.3 में समर्थित है!

निम्नलिखित स्निपेट ( डॉटनेट नमूनों से ) दर्शाता है कि:

public static Dictionary<int, string> EnumNamedValues<T>() where T : System.Enum
{
    var result = new Dictionary<int, string>();
    var values = Enum.GetValues(typeof(T));

    foreach (int item in values)
        result.Add(item, Enum.GetName(typeof(T), item));
    return result;
}

7.3 के संस्करण में अपने C # प्रोजेक्ट में अपना भाषा संस्करण सेट करना सुनिश्चित करें।


नीचे मूल उत्तर:

मुझे खेल में देर हो गई है, लेकिन मैंने इसे चुनौती के रूप में लिया कि यह कैसे हो सकता है। यह C # (या VB.NET में संभव नहीं है, लेकिन F # के लिए नीचे स्क्रॉल करें), लेकिन MSIL में संभव है। मैंने यह छोटा सा लिखा .... बात

// license: http://www.apache.org/licenses/LICENSE-2.0.html
.assembly MyThing{}
.class public abstract sealed MyThing.Thing
       extends [mscorlib]System.Object
{
  .method public static !!T  GetEnumFromString<valuetype .ctor ([mscorlib]System.Enum) T>(string strValue,
                                                                                          !!T defaultValue) cil managed
  {
    .maxstack  2
    .locals init ([0] !!T temp,
                  [1] !!T return_value,
                  [2] class [mscorlib]System.Collections.IEnumerator enumerator,
                  [3] class [mscorlib]System.IDisposable disposer)
    // if(string.IsNullOrEmpty(strValue)) return defaultValue;
    ldarg strValue
    call bool [mscorlib]System.String::IsNullOrEmpty(string)
    brfalse.s HASVALUE
    br RETURNDEF         // return default it empty

    // foreach (T item in Enum.GetValues(typeof(T)))
  HASVALUE:
    // Enum.GetValues.GetEnumerator()
    ldtoken !!T
    call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    call class [mscorlib]System.Array [mscorlib]System.Enum::GetValues(class [mscorlib]System.Type)
    callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Array::GetEnumerator() 
    stloc enumerator
    .try
    {
      CONDITION:
        ldloc enumerator
        callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
        brfalse.s LEAVE

      STATEMENTS:
        // T item = (T)Enumerator.Current
        ldloc enumerator
        callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
        unbox.any !!T
        stloc temp
        ldloca.s temp
        constrained. !!T

        // if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        callvirt instance string [mscorlib]System.Object::ToString()
        callvirt instance string [mscorlib]System.String::ToLower()
        ldarg strValue
        callvirt instance string [mscorlib]System.String::Trim()
        callvirt instance string [mscorlib]System.String::ToLower()
        callvirt instance bool [mscorlib]System.String::Equals(string)
        brfalse.s CONDITION
        ldloc temp
        stloc return_value
        leave.s RETURNVAL

      LEAVE:
        leave.s RETURNDEF
    }
    finally
    {
        // ArrayList's Enumerator may or may not inherit from IDisposable
        ldloc enumerator
        isinst [mscorlib]System.IDisposable
        stloc.s disposer
        ldloc.s disposer
        ldnull
        ceq
        brtrue.s LEAVEFINALLY
        ldloc.s disposer
        callvirt instance void [mscorlib]System.IDisposable::Dispose()
      LEAVEFINALLY:
        endfinally
    }

  RETURNDEF:
    ldarg defaultValue
    stloc return_value

  RETURNVAL:
    ldloc return_value
    ret
  }
} 

जो एक फ़ंक्शन उत्पन्न करता है जो इस तरह दिखाई देगा , यदि यह मान्य C #:

T GetEnumFromString<T>(string valueString, T defaultValue) where T : Enum

फिर निम्नलिखित C # कोड के साथ:

using MyThing;
// stuff...
private enum MyEnum { Yes, No, Okay }
static void Main(string[] args)
{
    Thing.GetEnumFromString("No", MyEnum.Yes); // returns MyEnum.No
    Thing.GetEnumFromString("Invalid", MyEnum.Okay);  // returns MyEnum.Okay
    Thing.GetEnumFromString("AnotherInvalid", 0); // compiler error, not an Enum
}

दुर्भाग्यवश, इसका अर्थ है कि आपके कोड का यह भाग C # के बजाय MSIL में लिखा गया है, केवल जोड़ा गया लाभ यह है कि आप इस विधि को करने में सक्षम हैं System.Enum। यह भी एक प्रकार का बमर है, क्योंकि यह एक अलग विधानसभा में संकलित हो जाता है। हालांकि, इसका मतलब यह नहीं है कि आपको इसे इस तरह से तैनात करना होगा।

लाइन को हटाकर .assembly MyThing{}और इलमास को निम्नानुसार लागू किया जाता है:

ilasm.exe /DLL /OUTPUT=MyThing.netmodule

आपको असेंबली के बजाय नेटमॉडल मिलता है।

दुर्भाग्य से, VS2010 (और पहले, जाहिर है) नेटमॉडल संदर्भों को जोड़ने का समर्थन नहीं करता है, जिसका अर्थ है कि आपको डिबगिंग करते समय इसे 2 अलग-अलग विधानसभाओं में छोड़ना होगा। एकमात्र तरीका है कि आप उन्हें अपनी विधानसभा के भाग के रूप में जोड़ सकते हैं /addmodule:{files}कमांड लाइन तर्क का उपयोग करके खुद को csc.exe चलाना होगा । यह MSBuild स्क्रिप्ट में बहुत दर्दनाक नहीं होगा । बेशक, यदि आप बहादुर या मूर्ख हैं, तो आप हर बार स्वयं सीबीएस चला सकते हैं। और यह निश्चित रूप से अधिक जटिल हो जाता है क्योंकि कई विधानसभाओं को इसकी पहुंच की आवश्यकता होती है।

तो, यह .Net में किया जा सकता है। क्या यह अतिरिक्त प्रयास के लायक है? उम, ठीक है, मुझे लगता है कि मैं आपको उस पर फैसला करने दूँगा।


विकल्प के रूप में एफ # समाधान

अतिरिक्त क्रेडिट: यह पता चला है कि enumMSIL के अलावा कम से कम एक अन्य .NET भाषा में एक सामान्य प्रतिबंध संभव है: F #।

type MyThing =
    static member GetEnumFromString<'T when 'T :> Enum> str defaultValue: 'T =
        /// protect for null (only required in interop with C#)
        let str = if isNull str then String.Empty else str

        Enum.GetValues(typedefof<'T>)
        |> Seq.cast<_>
        |> Seq.tryFind(fun v -> String.Compare(v.ToString(), str.Trim(), true) = 0)
        |> function Some x -> x | None -> defaultValue

यह बनाए रखने के लिए आसान है क्योंकि यह पूर्ण विज़ुअल स्टूडियो आईडीई समर्थन के साथ एक प्रसिद्ध भाषा है, लेकिन आपको अभी भी इसके समाधान में एक अलग परियोजना की आवश्यकता है। हालांकि, यह स्वाभाविक रूप से काफी अलग आईएल (कोड का उत्पादन है बहुत अलग) है और यह पर निर्भर करता है FSharp.Coreपुस्तकालय है, जो, बस किसी भी अन्य बाहरी पुस्तकालय की तरह, अपने वितरण का हिस्सा बनने के लिए की जरूरत है।

यहां बताया गया है कि आप इसका उपयोग कैसे कर सकते हैं (मूल रूप से एमएसआईएल समाधान के समान), और यह दिखाने के लिए कि यह अन्यथा पर्यायवाची मॉडल पर सही ढंग से विफल रहता है:

// works, result is inferred to have type StringComparison
var result = MyThing.GetEnumFromString("OrdinalIgnoreCase", StringComparison.Ordinal);
// type restriction is recognized by C#, this fails at compile time
var result = MyThing.GetEnumFromString("OrdinalIgnoreCase", 42);

67
हाँ, बहुत कट्टर। मेरे पास किसी ऐसे व्यक्ति के लिए अत्यंत सम्मान है जो IL में कोड कर सकता है, और यह जान सकता है कि उच्च भाषा स्तर पर सुविधाओं का समर्थन कैसे किया जाता है - एक स्तर जो हम में से कई अभी भी अनुप्रयोगों, व्यावसायिक नियमों, UI, घटक पुस्तकालयों, आदि के तहत निम्न स्तर के रूप में देखते हैं। ।
TonyG

13
मैं वास्तव में जानना चाहूंगा कि सी # टीम ने अभी तक इसकी अनुमति क्यों नहीं दी है, क्योंकि यह पहले से ही एमएसआईएल द्वारा समर्थित है।
मगसम

25
@MgSam - एरिक लिपर्ट से :There's no particularly unusual reason why not; we have lots of other things to do, limited budgets, and this one has never made it past the "wouldn't this be nice?" discussion in the language design team.
क्रिस्टोफर क्यूरेंस

5
@LordofScripts: मुझे लगता है कारण यह है कि कि जब एक वर्ग है जो एक रोकें है Tकरने के लिए System.Enumके साथ सभी काम करने के लिए सक्षम नहीं होगा Tकि लोगों को उम्मीद कर सकते हैं, सी # के लेखकों लगा वे साथ ही इसे पूरी तरह मना कर सकते हैं। मैं निर्णय को दुर्भाग्यपूर्ण मानता हूं, क्योंकि सी # ने System.Enumबाधाओं की किसी भी विशेष हैंडलिंग को केवल नजरअंदाज कर दिया था , यह एक HasAnyFlags<T>(this T it, T other)विस्तार विधि लिखना संभव था जो कि परिमाण के आदेशों की तुलना में तेजी से Enum.HasFlag(Enum)और किस प्रकार के अपने तर्कों की जांच करता था।
सुपरकैट

9
मुझे नहीं लगता कि मैंने कभी एक परियोजना की है जहाँ मैं यहाँ समाप्त नहीं हुआ। C # 6 110% सिंथैटिक शुगर है और इसमें नहीं मिला? बकवास काटो।
माइकल ब्लैकबर्न

214

C # ≥ 7.3

C # 7.3 (विजुअल स्टूडियो 2017 .7 v15.7 के साथ उपलब्ध) के साथ शुरू, यह कोड अब पूरी तरह से मान्य है:

public static TEnum Parse<TEnum>(string value)
    where TEnum : struct, Enum
{
 ...
}

C # ≤ 7.2

आपके पास एक वास्तविक संकलित प्रवर्तित एन्युम बाधा हो सकती है, जिससे अवरोध विरासत का दुरुपयोग हो सकता है। निम्नलिखित कोड classएक structही समय में दोनों और बाधाओं को निर्दिष्ट करता है:

public abstract class EnumClassUtils<TClass>
where TClass : class
{

    public static TEnum Parse<TEnum>(string value)
    where TEnum : struct, TClass
    {
        return (TEnum) Enum.Parse(typeof(TEnum), value);
    }

}

public class EnumUtils : EnumClassUtils<Enum>
{
}

उपयोग:

EnumUtils.Parse<SomeEnum>("value");

नोट: यह विशेष रूप से C # 5.0 भाषा विनिर्देश में कहा गया है:

यदि टाइप पैरामीटर S टाइप पैरामीटर T पर निर्भर करता है तो: [...] S के लिए मान्य है कि प्रकार प्रकार का अवरोध और संदर्भ प्रकार की बाधा के लिए T है। प्रभावी रूप से यह T को टाइप System.Object, System.ValueType, System.Enum, और किसी भी इंटरफ़ेस प्रकार तक सीमित करता है।


7
@ DavidI.McIntosh EnumClassUtils<System.Enum>किसी भी System.Enumऔर किसी भी प्रकार से टी को प्रतिबंधित करने के लिए पर्याप्त है । उसके बाद इसे आगे एक वास्तविक एनम प्रकार structपर Parseप्रतिबंधित करता है। आपको Enumकुछ बिंदु पर प्रतिबंधित करने की आवश्यकता है । ऐसा करने के लिए, आपकी कक्षा को नेस्टेड करना होगा। देखें gist.github.com/MrJul/7da12f5f2d6c69f03d79
Julien Lebosquain

7
बस स्पष्ट होने के लिए, मेरी टिप्पणी "सुखद नहीं" आपके समाधान पर एक टिप्पणी नहीं थी - यह वास्तव में एक सुंदर हैक है। बस "सुखद नहीं" कि एमएस हमें इस तरह के एक जटिल हैक का उपयोग करने के लिए मजबूर करता है।
डेविड आई। मैकिंटोश

2
क्या विस्तार के तरीकों के लिए भी उपयोगी होने के लिए यह काम करने का एक तरीका है?
मॉर्ड जुबेर

3
where TClass : classयहां अड़चन क्या है ?
tsemer

2
@Trinkyoenum DefinitelyNotAnInt : byte { Realize, That, I, Am, Not, An, Int } enum AlsoNotAnInt : long { Well, Bummer }
M.Stramm 12

30

संपादित करें

इस सवाल का जवाब अब जूलियन लेबोसक्वीन ने शानदार तरीके से दिया है । मैं भी साथ अपने जवाब विस्तार करना चाहते हैं ignoreCase, defaultValueऔर वैकल्पिक तर्क जोड़ते TryParseऔर ParseOrDefault

public abstract class ConstrainedEnumParser<TClass> where TClass : class
// value type constraint S ("TEnum") depends on reference type T ("TClass") [and on struct]
{
    // internal constructor, to prevent this class from being inherited outside this code
    internal ConstrainedEnumParser() {}
    // Parse using pragmatic/adhoc hard cast:
    //  - struct + class = enum
    //  - 'guaranteed' call from derived <System.Enum>-constrained type EnumUtils
    public static TEnum Parse<TEnum>(string value, bool ignoreCase = false) where TEnum : struct, TClass
    {
        return (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase);
    }
    public static bool TryParse<TEnum>(string value, out TEnum result, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T
    {
        var didParse = Enum.TryParse(value, ignoreCase, out result);
        if (didParse == false)
        {
            result = defaultValue;
        }
        return didParse;
    }
    public static TEnum ParseOrDefault<TEnum>(string value, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T
    {
        if (string.IsNullOrEmpty(value)) { return defaultValue; }
        TEnum result;
        if (Enum.TryParse(value, ignoreCase, out result)) { return result; }
        return defaultValue;
    }
}

public class EnumUtils: ConstrainedEnumParser<System.Enum>
// reference type constraint to any <System.Enum>
{
    // call to parse will then contain constraint to specific <System.Enum>-class
}

उपयोग के उदाहरण:

WeekDay parsedDayOrArgumentException = EnumUtils.Parse<WeekDay>("monday", ignoreCase:true);
WeekDay parsedDayOrDefault;
bool didParse = EnumUtils.TryParse<WeekDay>("clubs", out parsedDayOrDefault, ignoreCase:true);
parsedDayOrDefault = EnumUtils.ParseOrDefault<WeekDay>("friday", ignoreCase:true, defaultValue:WeekDay.Sunday);

पुराना

टिप्पणियों और 'नए' घटनाक्रमों का उपयोग करके विवेक के उत्तर पर मेरा पुराना सुधार :

  • TEnumउपयोगकर्ताओं के लिए स्पष्टता के लिए उपयोग करें
  • अतिरिक्त बाधा-जाँच के लिए अधिक इंटरफ़ेस-बाधाएँ जोड़ें
  • जाने TryParseसंभाल ignoreCaseमौजूदा पैरामीटर के साथ (VS2010 में शुरू / नेट 4)
  • वैकल्पिक रूप से जेनेरिक defaultमान (VS2005 / .net 2 में पेश किया गया) का उपयोग करें
  • का उपयोग वैकल्पिक तर्क मूलभूत मूल्यों के साथ (VS2010 / नेट 4 में प्रस्तुत), के लिए defaultValueऔरignoreCase

जिसके परिणामस्वरूप:

public static class EnumUtils
{
    public static TEnum ParseEnum<TEnum>(this string value,
                                         bool ignoreCase = true,
                                         TEnum defaultValue = default(TEnum))
        where TEnum : struct,  IComparable, IFormattable, IConvertible
    {
        if ( ! typeof(TEnum).IsEnum) { throw new ArgumentException("TEnum must be an enumerated type"); }
        if (string.IsNullOrEmpty(value)) { return defaultValue; }
        TEnum lResult;
        if (Enum.TryParse(value, ignoreCase, out lResult)) { return lResult; }
        return defaultValue;
    }
}

18

आप क्लास के लिए एक स्थिर कंस्ट्रक्टर को परिभाषित कर सकते हैं जो यह जांच करेगा कि टाइप टी एक एनम है और अपवाद नहीं है अगर यह नहीं है। यह जेफरी रिक्टर द्वारा सी # के माध्यम से अपनी पुस्तक सीएलआर में उल्लिखित विधि है।

internal sealed class GenericTypeThatRequiresAnEnum<T> {
    static GenericTypeThatRequiresAnEnum() {
        if (!typeof(T).IsEnum) {
        throw new ArgumentException("T must be an enumerated type");
        }
    }
}

तब पार्स विधि में, आप स्ट्रिंग से एनम में बदलने के लिए Enum.Parse (टाइपोफ (T), इनपुट, ट्रू) का उपयोग कर सकते हैं। अंतिम सच्चा पैरामीटर इनपुट के मामले की अनदेखी के लिए है।


1
यह सामान्य वर्गों के लिए एक अच्छा विकल्प है - लेकिन निश्चित रूप से, यह सामान्य तरीकों के लिए मदद नहीं करता है।
मैकगर्लगेल

इसके अलावा, यह भी संकलन के समय पर लागू नहीं किया जाता है, आप केवल यह जान पाएंगे Enum Tकि कंस्ट्रक्टर निष्पादित होने पर आप एक गैर प्रदान करते हैं। हालांकि यह एक उदाहरण के निर्माणकर्ता की प्रतीक्षा की तुलना में बहुत अच्छा है।
jrh

15

यह भी विचार किया जाना चाहिए कि Enum बाधाओं का उपयोग कर C # 7.3 की रिहाई के बाद से अतिरिक्त चेकिंग और सामान किए बिना आउट-ऑफ-द-बॉक्स का समर्थन किया जाता है।

इसलिए आगे जाकर आपने अपनी परियोजना के भाषा संस्करण को C # 7.3 में बदल दिया है, निम्नलिखित कोड पूरी तरह से ठीक काम करने वाला है:

    private static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
    {
        // Your code goes here...
    }

यदि आप नहीं जानते कि भाषा संस्करण को C # 7.3 में कैसे बदलना है, तो निम्न स्क्रीनशॉट देखें: यहां छवि विवरण दर्ज करें

EDIT 1 - आवश्यक विज़ुअल स्टूडियो संस्करण और विचारशील

नए सिंटैक्स को पहचानने के लिए Visual Studio के लिए आपको कम से कम 15.7 संस्करण की आवश्यकता होती है। आप Microsoft के रिलीज़ नोटों में भी इसका उल्लेख कर सकते हैं, दृश्य स्टूडियो 2017 15.7 रिलीज़ नोट्स देखें । इस वैध प्रश्न को इंगित करने के लिए @MohamedElshawaf का धन्यवाद।

Pls यह भी ध्यान दें कि मेरे मामले में ReSharper 2018.1 इस EDIT को लिखने के रूप में अभी तक C # 7.3 का समर्थन नहीं करता है। ReSharper सक्रिय होने के कारण यह Enum बाधा को उजागर करता है, यह बताने में एक त्रुटि है कि मुझे 'System.Array', 'System.Delegate', 'System.Enum', 'System.ValueType', 'ऑब्जेक्ट' को 'पैरामीटर पैरामीटर बाधा' के रूप में उपयोग नहीं किया जा सकता है । ReSharper विधि के प्रकार पैरा टी के 'Enum' बाधा को दूर करने के लिए एक त्वरित समाधान के रूप में सुझाव देता है

हालाँकि, यदि आप उपकरण के तहत अस्थायी रूप से ReSharper को बंद करते हैं -> विकल्प -> ReSharper अंतिम -> सामान्य आप देखेंगे कि वाक्यविन्यास पूरी तरह से ठीक है कि आप VS 15.7 या उच्चतर और C # 7.3 या उच्चतर का उपयोग करते हैं।


1
आप किस VS संस्करण का उपयोग कर रहे हैं?
mshwf

1
@MohamedElshawaf मेरा मानना ​​है कि यह संस्करण 15.7 है जिसमें C # 7.3 के लिए समर्थन शामिल है
पैट्रिक रॉबर्ट्स

1
मुझे लगता है कि टाइप पैरामीटर के रूप में खुद को where T : struct, Enumपारित करने से बचने के लिए लिखना बेहतर है System.Enum
मारियस पावेल्स्की

जैसे @MariuszPawelski मैं लिखता हूं struct, Enum। मेरे तर्क को यहाँ उत्तर और टिप्पणियों में समझाया गया है
स्टीफन कैनेडी

ReSharper जानकारी वास्तव में मेरी मदद की। नोट नवीनतम पूर्वावलोकन संस्करण इस सुविधा का समर्थन करता है।
DalSoft

11

मैंने डायमरज़िओनिस्ट द्वारा नमूने को संशोधित किया। यह संस्करण केवल Enums के साथ काम करेगा और इसके माध्यम से संरचनाओं को प्राप्त नहीं होने देगा।

public static T ParseEnum<T>(string enumString)
    where T : struct // enum 
    {
    if (String.IsNullOrEmpty(enumString) || !typeof(T).IsEnum)
       throw new Exception("Type given must be an Enum");
    try
    {

       return (T)Enum.Parse(typeof(T), enumString, true);
    }
    catch (Exception ex)
    {
       return default(T);
    }
}

13
मैं विफलता पर डिफ़ॉल्ट मान नहीं लौटाऊंगा; मैं अपवाद का प्रचार करने देता (जैसा कि यह Enum.Parse के साथ करता है)। इसके बजाय, TryParse को एक बूल लौटाने का उपयोग करें और एक आउट परम का उपयोग करके परिणाम लौटाएं।
मार्क सिम्पसन

1
ओपी चाहता है कि यह मामला असंवेदनशील हो, यह नहीं है।
कोनराड मोरावस्की

9

मैंने कोड को थोड़ा सुधारने की कोशिश की:

public T LoadEnum<T>(string value, T defaultValue = default(T)) where T : struct, IComparable, IFormattable, IConvertible
{
    if (Enum.IsDefined(typeof(T), value))
    {
        return (T)Enum.Parse(typeof(T), value, true);
    }
    return defaultValue;
}

1
यह स्वीकृत उत्तर से बेहतर है क्योंकि यह आपको कॉल करने की अनुमति देता है, defaultValue.ToString("D", System.Globalization.NumberFormatInfo.CurrentInfo)भले ही आपको पता न हो कि यह किस प्रकार का है, केवल यह है कि ऑब्जेक्ट एक एनम है।
स्टाइल

1
हालांकि, अग्रिम जांच IsDefinedमामले की संवेदनशीलता को बर्बाद कर देगी। इसके विपरीत Parse, IsDefinedकोई ignoreCaseतर्क नहीं है, और MSDN का कहना है कि यह केवल सटीक मामले से मेल खाता है
Nyerguds 15

5

मुझे विशिष्ट आवश्यकता है जहां मुझे एनम मान से जुड़े पाठ के साथ एनम का उपयोग करने की आवश्यकता है। उदाहरण के लिए जब मैं त्रुटि विवरण को निर्दिष्ट करने के लिए enum का उपयोग करता हूं तो त्रुटि विवरण का वर्णन करना आवश्यक है।

public static class XmlEnumExtension
{
    public static string ReadXmlEnumAttribute(this Enum value)
    {
        if (value == null) throw new ArgumentNullException("value");
        var attribs = (XmlEnumAttribute[]) value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof (XmlEnumAttribute), true);
        return attribs.Length > 0 ? attribs[0].Name : value.ToString();
    }

    public static T ParseXmlEnumAttribute<T>(this string str)
    {
        foreach (T item in Enum.GetValues(typeof(T)))
        {
            var attribs = (XmlEnumAttribute[])item.GetType().GetField(item.ToString()).GetCustomAttributes(typeof(XmlEnumAttribute), true);
            if(attribs.Length > 0 && attribs[0].Name.Equals(str)) return item;
        }
        return (T)Enum.Parse(typeof(T), str, true);
    }
}

public enum MyEnum
{
    [XmlEnum("First Value")]
    One,
    [XmlEnum("Second Value")]
    Two,
    Three
}

 static void Main()
 {
    // Parsing from XmlEnum attribute
    var str = "Second Value";
    var me = str.ParseXmlEnumAttribute<MyEnum>();
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
    // Parsing without XmlEnum
    str = "Three";
    me = str.ParseXmlEnumAttribute<MyEnum>();
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
    me = MyEnum.One;
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
}

4

आशा है कि यह उपयोगी है:

public static TValue ParseEnum<TValue>(string value, TValue defaultValue)
                  where TValue : struct // enum 
{
      try
      {
            if (String.IsNullOrEmpty(value))
                  return defaultValue;
            return (TValue)Enum.Parse(typeof (TValue), value);
      }
      catch(Exception ex)
      {
            return defaultValue;
      }
}

1
आप मामले असंवेदनशीलता की जरूरत है, बस की जगह return (TValue)Enum.Parse(typeof (TValue), value);सेreturn (TValue)Enum.Parse(typeof (TValue), value, true);
पाउलो सैंटोस

3

दिलचस्प रूप से पर्याप्त है, जाहिरा तौर पर यह अन्य भाषा में संभव है (प्रबंधित C ++, IL सीधे)।

उद्धरण के लिए:

... दोनों अड़चनें वास्तव में वैध आईएल का उत्पादन करती हैं और सी # द्वारा भी उपभोग किया जा सकता है यदि किसी अन्य भाषा में लिखा गया है (आप उन बाधाओं को प्रबंधित सी ++ या आईएल में घोषित कर सकते हैं)।

कौन जाने


2
C ++ के लिए प्रबंधित एक्सटेंशन में जेनरिक का कोई समर्थन नहीं है, मुझे लगता है कि आपका मतलब C ++ / CLI है।
बेन वोइगट

3

यह मेरा इस पर लेना है। उत्तरों और MSDN से संयुक्त

public static TEnum ParseToEnum<TEnum>(this string text) where TEnum : struct, IConvertible, IComparable, IFormattable
{
    if (string.IsNullOrEmpty(text) || !typeof(TEnum).IsEnum)
        throw new ArgumentException("TEnum must be an Enum type");

    try
    {
        var enumValue = (TEnum)Enum.Parse(typeof(TEnum), text.Trim(), true);
        return enumValue;
    }
    catch (Exception)
    {
        throw new ArgumentException(string.Format("{0} is not a member of the {1} enumeration.", text, typeof(TEnum).Name));
    }
}

MSDN स्रोत


2
यह वास्तव में मतलब नहीं है। यदि TEnumवास्तव में एक Enum प्रकार है, लेकिन textएक खाली स्ट्रिंग है तो आपको एक ArgumentExceptionकहावत मिलती है "TEnum एक Enum प्रकार होना चाहिए" भले ही वह है।
निक

3

मौजूदा उत्तर C # <= 7.2 के रूप में सही हैं। हालाँकि, निम्नलिखित की अनुमति देने के लिए C # भाषा सुविधा अनुरोध (एक Corefx सुविधा अनुरोध से बंधा हुआ ) है;

public class MyGeneric<TEnum> where TEnum : System.Enum
{ }

लेखन के समय, सुविधा भाषा विकास बैठकों में "चर्चा में" है।

संपादित करें

के अनुसार nawfal की जानकारी, इस सी # में पेश किया जा रहा है 7.3


1
दिलचस्प चर्चा वहाँ, धन्यवाद। पत्थरों में कुछ भी सेट नहीं है हालांकि (अभी तक)
जॉन

1
@johnc, बिल्कुल सच है, लेकिन लायक एक नोट और यह है एक अक्सर पूछे जाने वाले सुविधा। इस पर मेला लगता है।
डिस्कजुनकी


1

मुझे यह हमेशा पसंद आया (आप उपयुक्त रूप में संशोधित कर सकते हैं):

public static IEnumerable<TEnum> GetEnumValues()
{
  Type enumType = typeof(TEnum);

  if(!enumType.IsEnum)
    throw new ArgumentException("Type argument must be Enum type");

  Array enumValues = Enum.GetValues(enumType);
  return enumValues.Cast<TEnum>();
}

1

मैं IL का उपयोग करके क्रिस्टोफर क्यूरेंस के समाधान से प्यार करता था, लेकिन उन लोगों के लिए जो अपने निर्माण की प्रक्रिया में MSIL सहित मुश्किल व्यापार से निपटना नहीं चाहते हैं, मैंने C # में समान फ़ंक्शन लिखा था।

कृपया ध्यान दें कि आप जेनेरिक प्रतिबंध का उपयोग नहीं कर सकते where T : Enumक्योंकि Enum विशेष प्रकार है। इसलिए मुझे यह जांचना होगा कि क्या दिया गया जेनेरिक टाइप वास्तव में एनम है।

मेरा कार्य है:

public static T GetEnumFromString<T>(string strValue, T defaultValue)
{
    // Check if it realy enum at runtime 
    if (!typeof(T).IsEnum)
        throw new ArgumentException("Method GetEnumFromString can be used with enums only");

    if (!string.IsNullOrEmpty(strValue))
    {
        IEnumerator enumerator = Enum.GetValues(typeof(T)).GetEnumerator();
        while (enumerator.MoveNext())
        {
            T temp = (T)enumerator.Current;
            if (temp.ToString().ToLower().Equals(strValue.Trim().ToLower()))
                return temp;
        }
    }

    return defaultValue;
}

1

मैंने विवेक के समाधान को एक उपयोगिता वर्ग में शामिल किया है जिसे आप पुन: उपयोग कर सकते हैं। कृपया ध्यान दें कि आपको अभी भी अपने प्रकार पर "जहां टी: संरचना, असंगत" प्रकार की बाधाओं को परिभाषित करना चाहिए।

using System;

internal static class EnumEnforcer
{
    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="typeParameterName">Name of the type parameter.</param>
    /// <param name="methodName">Name of the method which accepted the parameter.</param>
    public static void EnforceIsEnum<T>(string typeParameterName, string methodName)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            string message = string.Format(
                "Generic parameter {0} in {1} method forces an enumerated type. Make sure your type parameter {0} is an enum.",
                typeParameterName,
                methodName);

            throw new ArgumentException(message);
        }
    }

    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="typeParameterName">Name of the type parameter.</param>
    /// <param name="methodName">Name of the method which accepted the parameter.</param>
    /// <param name="inputParameterName">Name of the input parameter of this page.</param>
    public static void EnforceIsEnum<T>(string typeParameterName, string methodName, string inputParameterName)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            string message = string.Format(
                "Generic parameter {0} in {1} method forces an enumerated type. Make sure your input parameter {2} is of correct type.",
                typeParameterName,
                methodName,
                inputParameterName);

            throw new ArgumentException(message);
        }
    }

    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="exceptionMessage">Message to show in case T is not an enum.</param>
    public static void EnforceIsEnum<T>(string exceptionMessage)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException(exceptionMessage);
        }
    }
}

1

मैं एक विस्तार विधि बनाया to get integer value from enum विधि कार्यान्वयन को देखो

public static int ToInt<T>(this T soure) where T : IConvertible//enum
{
    if (typeof(T).IsEnum)
    {
        return (int) (IConvertible)soure;// the tricky part
    }
    //else
    //    throw new ArgumentException("T must be an enumerated type");
    return soure.ToInt32(CultureInfo.CurrentCulture);
}

यह उपयोग है

MemberStatusEnum.Activated.ToInt()// using extension Method
(int) MemberStatusEnum.Activated //the ordinary way

जबकि यह शायद काम करता है, यह सवाल के लिए लगभग कोई प्रासंगिकता नहीं है।
quetzalcoatl

1

जैसा कि पहले अन्य उत्तरों में कहा गया है; हालांकि यह स्रोत-कोड में व्यक्त नहीं किया जा सकता है, यह वास्तव में IL स्तर पर किया जा सकता है। @Christopher Currens उत्तर दिखाता है कि IL किस तरह से काम करता है।

साथ Fody रों ऐड-में ExtraConstraints.Fody वहाँ एक बहुत ही सरल तरीके से, निर्माण टूलींग के साथ पूरा, इस लक्ष्य को हासिल करने के लिए है। बस अपने प्रोजेक्ट में उनके नगेट पैकेज ( Fody, ExtraConstraints.Fody) जोड़ें और निम्नानुसार बाधाओं को जोड़ें (एक्स्ट्राकॉन्स्ट्रेट की रीडमी से अंश):

public void MethodWithEnumConstraint<[EnumConstraint] T>() {...}

public void MethodWithTypeEnumConstraint<[EnumConstraint(typeof(ConsoleColor))] T>() {...}

और फोडी को उपस्थित होने के लिए आवश्यक आईएल को जोड़ना होगा। विवश प्रतिनिधियों की अतिरिक्त सुविधा पर भी ध्यान दें:

public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
{...}

public void MethodWithTypeDelegateConstraint<[DelegateConstraint(typeof(Func<int>))] T> ()
{...}

Enums के बारे में, आप अत्यधिक दिलचस्प Enums.NET का भी ध्यान रखना चाह सकते हैं


1

यह मेरा कार्यान्वयन है। मूल रूप से, आप किसी भी विशेषता को सेटअप कर सकते हैं और यह काम करता है।

public static class EnumExtensions
    {
        public static string GetDescription(this Enum @enum)
        {
            Type type = @enum.GetType();
            FieldInfo fi = type.GetField(@enum.ToString());
            DescriptionAttribute[] attrs =
                fi.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[];
            if (attrs.Length > 0)
            {
                return attrs[0].Description;
            }
            return null;
        }
    }

0

यदि बाद में सीधे कास्टिंग का उपयोग करना ठीक है, तो मुझे लगता है कि आप System.Enumअपनी विधि में आधार वर्ग का उपयोग कर सकते हैं , जहां भी आवश्यक हो। आपको बस प्रकार के मापदंडों को सावधानीपूर्वक बदलने की आवश्यकता है। तो विधि कार्यान्वयन निम्नानुसार होगा:

public static class EnumUtils
{
    public static Enum GetEnumFromString(string value, Enum defaultValue)
    {
        if (string.IsNullOrEmpty(value)) return defaultValue;
        foreach (Enum item in Enum.GetValues(defaultValue.GetType()))
        {
            if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        }
        return defaultValue;
    }
}

तब आप इसका उपयोग कर सकते हैं जैसे:

var parsedOutput = (YourEnum)EnumUtils.GetEnumFromString(someString, YourEnum.DefaultValue);

उपयोग से Enum.ToObject()अधिक लचीले परिणाम प्राप्त होंगे। जिसमें जोड़ा गया है, आप मामले की संवेदनशीलता के बिना स्ट्रिंग तुलना कर सकते हैं जो कॉल करने की आवश्यकता को नकार देगाToLower()
DiskJunky

-6

पूर्णता के लिए, निम्नलिखित एक जावा समाधान है। मुझे लगता है कि C # में भी ऐसा ही किया जा सकता है। यह कोड में कहीं भी प्रकार निर्दिष्ट करने से बचता है - इसके बजाय, आप इसे उन तारों में निर्दिष्ट करते हैं जिन्हें आप पार्स करने की कोशिश कर रहे हैं।

समस्या यह है कि यह जानने का कोई तरीका नहीं है कि स्ट्रिंग किस गणना से मेल खाती है - इसलिए इसका उत्तर उस समस्या को हल करना है।

केवल स्ट्रिंग मान को स्वीकार करने के बजाय, एक स्ट्रिंग को स्वीकार करें, जिसमें "गणना" के रूप में गणना और मान दोनों हैं। वर्किंग कोड नीचे है - जावा 1.8 या बाद की आवश्यकता है। यह एक्सएमएल को और अधिक सटीक बना देगा क्योंकि आप केवल रंग = "लाल" के बजाय रंग = "रंग" जैसे कुछ देखेंगे।

आप एंम नाम डॉट मान नाम वाले स्ट्रिंग के साथ एक्सेमरनेटेडवैल्यू () पद्धति को कॉल करेंगे।

विधि औपचारिक गणना मूल्य लौटाती है।

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;


public class EnumFromString {

    enum NumberEnum {One, Two, Three};
    enum LetterEnum {A, B, C};


    Map<String, Function<String, ? extends Enum>> enumsByName = new HashMap<>();

    public static void main(String[] args) {
        EnumFromString efs = new EnumFromString();

        System.out.print("\nFirst string is NumberEnum.Two - enum is " + efs.acceptEnumeratedValue("NumberEnum.Two").name());
        System.out.print("\nSecond string is LetterEnum.B - enum is " + efs.acceptEnumeratedValue("LetterEnum.B").name());

    }

    public EnumFromString() {
        enumsByName.put("NumberEnum", s -> {return NumberEnum.valueOf(s);});
        enumsByName.put("LetterEnum", s -> {return LetterEnum.valueOf(s);});
    }

    public Enum acceptEnumeratedValue(String enumDotValue) {

        int pos = enumDotValue.indexOf(".");

        String enumName = enumDotValue.substring(0, pos);
        String value = enumDotValue.substring(pos + 1);

        Enum enumeratedValue = enumsByName.get(enumName).apply(value);

        return enumeratedValue;
    }


}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.