it-swarm.it

Crea un metodo generico che vincola T a un Enum

Sto costruendo una funzione per estendere il concetto Enum.Parse

  • Consente di analizzare un valore predefinito nel caso in cui non venga trovato un valore Enum
  • È insensibile al maiuscolo/minuscolo

Quindi ho scritto quanto segue:

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

Ricevo che un vincolo di errore non può essere una classe speciale System.Enum.

Abbastanza corretto, ma c'è una soluzione alternativa per consentire un Enum generico, o dovrò dover imitare la funzione Parse e passare un tipo come attributo, che costringe il brutto requisito di boxing al codice.

EDITTutti i suggerimenti sottostanti sono stati molto apprezzati, grazie.

Ho risolto il problema (ho lasciato il ciclo per mantenere insensibilità alle maiuscole e minuscole - lo sto utilizzando durante l'analisi di XML)

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 feb 2015) Julien Lebosquain ha pubblicato di recente una soluzione generica di tipo safe-safe imposta compilatore in MSIL o F # below, che vale la pena dare un'occhiata e un upvote. Rimuoverò questa modifica se la soluzione bolle più in alto nella pagina.

1052
johnc

Dal momento che Enum tipo implementa l'interfaccia IConvertible, un'implementazione migliore dovrebbe essere qualcosa del genere:

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

   //...
}

Ciò consentirà comunque il passaggio di tipi di valore che implementano IConvertible. Le probabilità sono rare però.

931
Vivek

Questa funzionalità è finalmente supportata in C # 7.3!

Il seguente frammento (da gli esempi dotnet ) dimostra che usa:

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

Assicurati di impostare la versione della lingua nel tuo progetto C # alla versione 7.3.


Risposta originale qui sotto:

Sono in ritardo al gioco, ma ho considerato la sfida come vedere come si potrebbe fare. Non è possibile in C # (o VB.NET, ma scorrere verso il basso per F #), ma è possibile in MSIL. Ho scritto questa piccola cosa ....

// 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
  }
} 

Che genera una funzione che would assomiglia a questo, se fosse C # valido:

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

Quindi con il seguente codice 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
}

Sfortunatamente, questo significa che questa parte del tuo codice è scritta in MSIL anziché in C #, con l'unico vantaggio che puoi limitare questo metodo con System.Enum. È anche un po 'faticoso, perché viene compilato in un'assemblea separata. Tuttavia, ciò non significa che devi distribuirlo in questo modo.

Rimuovendo la riga .Assembly MyThing{} e invocando ilasm come segue:

ilasm.exe /DLL /OUTPUT=MyThing.netmodule

ottieni un modulo net invece di un assembly.

Sfortunatamente, VS2010 (e prima, ovviamente) non supporta l'aggiunta di riferimenti netmodule, il che significa che dovresti lasciarlo in 2 assembly separati quando esegui il debug. L'unico modo per aggiungerli come parte del tuo assieme sarebbe di eseguire csc.exe tu stesso usando l'argomento della riga di comando /addmodule:{files}. Non sarebbe too doloroso in uno script MSBuild. Naturalmente, se sei coraggioso o stupido, puoi eseguire Csc te stesso manualmente ogni volta. E certamente diventa più complicato in quanto più gruppi hanno bisogno di accedervi.

Quindi, può essere fatto in. Net. Vale la pena lo sforzo extra? Um, beh, immagino che ti lascerò decidere su quello.


F # Soluzione come alternativa

Credito extra: si scopre che una restrizione generica su enum è possibile in almeno un altro linguaggio .NET oltre a MSIL: 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

Questo è più facile da mantenere poiché è una lingua ben conosciuta con il pieno supporto di Visual Studio IDE, ma per la sua soluzione è comunque necessario un progetto separato. Tuttavia, produce in modo molto diverso IL (il codice è molto diverso) e si basa sulla libreria FSharp.Core, che, proprio come qualsiasi altra libreria esterna, deve diventare parte della distribuzione.

Ecco come puoi usarlo (fondamentalmente lo stesso della soluzione MSIL), e per mostrare che fallisce correttamente su strutture altrimenti sinonimi:

// 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);
525

C # ≥ 7.3

A partire da C # 7.3 (disponibile con Visual Studio 2017 ≥ v15.7), questo codice è ora completamente valido:

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

C # ≤ 7.2

È possibile avere un vincolo enum forzato del compilatore reale abusando dell'ereditarietà dei vincoli. Il seguente codice specifica sia i vincoli class che struct allo stesso tempo:

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>
{
}

Uso:

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

Nota: questo è specificamente indicato nelle specifiche del linguaggio C # 5.0:

Se il parametro di tipo S dipende dal parametro di tipo T, allora: [...] È valido per S avere il vincolo del tipo di valore e T di avere il vincolo del tipo di riferimento. In pratica ciò limita T ai tipi System.Object, System.ValueType, System.Enum e qualsiasi tipo di interfaccia.

181

Modifica

/ La domanda è stata superbamente risolta da Julien Lebosquain . Vorrei anche estendere la sua risposta con ignoreCasename__, defaultValuee gli argomenti opzionali, aggiungendo TryParsee ParseOrDefaultname__.

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
}

Esempi di utilizzo:

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

Vecchio

I miei vecchi miglioramenti su La risposta di Vivek usando i commenti e gli sviluppi 'nuovi':

  • usa TEnumper chiarezza per gli utenti
  • aggiungere ulteriori vincoli di interfaccia per ulteriori controlli dei vincoli
  • let TryParseNAME_ handle ignoreCasecon il parametro esistente (introdotto in VS2010/.Net 4)
  • facoltativamente usa il valore generico defaultNAME_ (introdotto in VS2005/.Net 2)
  • usa argomenti opzionali (introdotto in VS2010/.Net 4) con valori predefiniti, per defaultValuee ignoreCasename__

con il risultato di:

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;
    }
}
30
Yahoo Serious

È possibile definire un costruttore statico per la classe che verificherà che il tipo T è un enum e genera un'eccezione se non lo è. Questo è il metodo menzionato da Jeffery Richter nel suo libro CLR via C #.

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

Quindi, nel metodo di analisi, puoi semplicemente usare Enum.Parse (typeof (T), input, true) per convertire da string a enum. L'ultimo vero parametro è per ignorare il caso dell'input.

18
Karg

Va anche considerato che dal momento che il rilascio di C # 7.3 usando i vincoli Enum è supportato immediatamente senza dover fare controlli e cose aggiuntive.

Quindi, andando avanti e dato che hai cambiato la versione linguistica del tuo progetto in C # 7.3, il seguente codice funzionerà perfettamente:

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

Nel caso in cui non si sappia come modificare la versione della lingua in C # 7.3, vedere la seguente schermata:  enter image description here

MODIFICA 1 - Versione di Visual Studio richiesta e considerando ReSharper

Per consentire a Visual Studio di riconoscere la nuova sintassi, è necessaria almeno la versione 15.7. Puoi trovare quello menzionato anche nelle note di rilascio di Microsoft, vedi Note di rilascio di Visual Studio 2017 15.7 . Grazie a @MohamedElshawaf per aver indicato questa domanda valida.

Si noti inoltre che nel mio caso ReSharper 2018.1 al momento di scrivere questo EDIT non supporta ancora C # 7.3. Avendo ReSharper attivato evidenzia il vincolo Enum come errore che mi dice Non è possibile usare 'System.Array', 'System.Delegate', 'System.Enum', 'System.ValueType', 'object' come tipo parameter constraint. ReSharper suggerisce come una soluzione rapida a Rimuovi il vincolo 'Enum' del parametro di tipo T del metodo

Tuttavia, se disattivi ReSharper temporaneamente sotto Strumenti -> Opzioni -> ReSharper Ultimate -> Generale vedrai che la sintassi è perfetta, dato che usi VS 15.7 o superiore e C # 7.3 o superiore.

13
baumgarb

Ho modificato il campione con il dimarzionista. Questa versione funzionerà solo con Enums e non lascerà passare le strutture.

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

Ho provato a migliorare un po 'il codice:

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;
}
9
Martin

Ho dei requisiti specifici in cui ho richiesto di usare enum con testo associato al valore enum. Ad esempio quando uso enum per specificare il tipo di errore richiesto per descrivere i dettagli dell'errore.

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());
}
5
Sunny Rajwadi

Spero che questo sia utile:

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;
      }
}
4
dimarzionist

Questo è il mio modo di prenderlo. Combinato dalle risposte e 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));
    }
}

Origine MSDN

3
KarmaEDV

È interessante notare che apparentemente questo è possibile in altre lingue (gestito direttamente C++, IL).

Per citare:

... Entrambi i vincoli producono effettivamente IL valido e possono anche essere consumati da C # se scritti in un'altra lingua (è possibile dichiarare tali vincoli in C++ gestito o in IL).

Chissà

3
Andrew Backer

Le risposte esistenti sono vere da C # <= 7.2. Tuttavia, c'è una lingua C # richiesta di funzionalità (legata a una corefx richiesta di funzione) per consentire quanto segue;

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

Al momento della stesura, la funzione è "In discussione" durante le riunioni di sviluppo linguistico.

EDIT

Come da nawfal informazioni, questo viene introdotto in C # 7.3 .

3
DiskJunky

Ho incapsulato la soluzione di Vivek in una classe di utilità che è possibile riutilizzare. Si noti che è ancora necessario definire i vincoli di tipo "dove T: struct, IConvertible" sul proprio tipo.

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
niaher

Mi è sempre piaciuto (potresti modificare come appropriato):

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
Jeff

Come affermato in altre risposte prima; mentre questo non può essere espresso nel codice sorgente, può effettivamente essere fatto su IL Level. @Christopher Currens answer mostra come l'IL fa a quello.

Con Fody s Add-In ExtraConstraints.Fody c'è un modo molto semplice, completo di strumenti di costruzione, per raggiungere questo obiettivo. Basta aggiungere i pacchetti nuget (Fody, ExtraConstraints.Fody) al progetto e aggiungere i vincoli come segue (Estratto dal file Leggimi di ExtraConstraints):

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

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

e Fody aggiungerà l'IL necessario perché il vincolo sia presente. Nota anche la funzione aggiuntiva dei delegati vincolanti:

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

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

Per quanto riguarda le enumerazioni, potresti anche prendere nota del molto interessante Enums.NET .

1

Ho creato un metodo di estensione to get integer value from enum per dare un'occhiata all'implementazione del metodo

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

questo è l'uso

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

Ho adorato la soluzione di Christopher Currens usando IL, ma per coloro che non vogliono occuparsi del difficile processo di includere MSIL nel loro processo di compilazione, ho scritto una funzione simile in C #.

Si noti che non è possibile utilizzare restrizioni generiche come where T : Enum perché Enum è un tipo speciale. Quindi devo verificare se un dato tipo generico è davvero enumerato.

La mia funzione è:

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
expert

Se è corretto utilizzare il cast diretto, suppongo che tu possa usare la classe base System.Enum nel tuo metodo, ovunque sia necessario. Hai solo bisogno di sostituire con attenzione i parametri del tipo. Quindi l'implementazione del metodo sarebbe come:

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

Quindi puoi usarlo come:

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