web-dev-qa-db-fra.com

Créer une méthode générique contraignant T à un enum

Je construis une fonction pour étendre le concept Enum.Parse

  • Permet d'analyser une valeur par défaut au cas où une valeur Enum ne serait pas trouvée
  • Est insensible à la casse

Alors j'ai écrit ce qui suit:

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

Je reçois une contrainte d'erreur ne peut pas être une classe spéciale System.Enum.

C'est assez correct, mais existe-t-il une solution permettant d'autoriser un Enum générique, ou vais-je devoir imiter la fonction Parse et passer un type en tant qu'attribut, ce qui force l'exigence de la boxe laide à votre code.

EDIT Toutes les suggestions ci-dessous ont été grandement appréciées, merci.

J'ai réglé la question (j'ai laissé la boucle pour maintenir l'insensibilité à la casse - je l'utilise lors de l'analyse de 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 février 2015) Julien Lebosquain a récemment publié ne solution générique de sécurité du type imposée par le compilateur dans MSIL ou F # ci-dessous , qui vaut bien un coup d’oeil, et un vote positif. Je vais supprimer cette modification si la solution bouillonne plus haut sur la page.

1084
johnc

Puisque Enum Type implémente l'interface IConvertible, une meilleure implémentation devrait ressembler à ceci:

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

   //...
}

Cela permettra toujours de passer des types de valeur implémentant IConvertible. Les chances sont rares cependant.

948
Vivek

Cette fonctionnalité est enfin supportée en C # 7.3!

L'extrait suivant (de les exemples dotnet ) montre comment:

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

Veillez à définir la version linguistique de votre projet C # sur la version 7.3.


Réponse originale ci-dessous:

Je suis en retard pour le match, mais j’ai pris le défi de voir comment cela pourrait être fait. Ce n'est pas possible dans C # (ou VB.NET, mais faites défiler vers le bas pour F #), mais est possible dans MSIL. J'ai écrit cette petite ... chose

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

Ce qui génère une fonction qui ressemblerait à , si elle était valide C #:

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

Puis avec le code C # suivant:

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
}

Malheureusement, cela signifie que cette partie de votre code est écrite en MSIL au lieu de C #, avec le seul avantage supplémentaire que vous pouvez contraindre cette méthode avec System.Enum. C'est aussi un peu une déception, car il est compilé dans une assemblée séparée. Cependant, cela ne signifie pas que vous devez le déployer de cette façon.

En supprimant la ligne .Assembly MyThing{} et en appelant ilasm comme suit:

ilasm.exe /DLL /OUTPUT=MyThing.netmodule

vous obtenez un netmodule au lieu d'une assemblée.

Malheureusement, VS2010 (et les versions antérieures, bien sûr) ne prend pas en charge l’ajout de références netmodule, ce qui signifie que vous devez le laisser dans 2 assemblys distincts lors du débogage. La seule façon de les ajouter dans votre Assemblée serait d’exécuter vous-même csc.exe en utilisant l’argument de ligne de commande /addmodule:{files}. Cela ne serait pas trop pénible dans un script MSBuild. Bien sûr, si vous êtes courageux ou stupide, vous pouvez exécuter csc vous-même manuellement à chaque fois. Et cela devient certainement plus compliqué car plusieurs assemblées ont besoin d'y accéder.

Donc, cela peut être fait en .Net. Vaut-il l'effort supplémentaire? Euh, eh bien, je suppose que je vais vous laisser décider de celui-là.


Solution F # comme alternative

Crédit supplémentaire: il s'avère qu'une restriction générique sur enum est possible dans au moins un autre langage .NET en plus de 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

Celui-ci est plus facile à gérer car il s'agit d'un langage bien connu avec une prise en charge complète de Visual Studio IDE, mais vous avez toujours besoin d'un projet distinct dans votre solution. Cependant, il produit naturellement des IL très différents (le code est très différent) et repose sur la bibliothèque FSharp.Core, qui, comme toute autre bibliothèque externe, doit faire partie de votre distribution.

Voici comment vous pouvez l'utiliser (essentiellement la même chose que la solution MSIL) et montrer qu'elle échoue correctement sur des structures qui ne sont pas synonymes:

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

C # ≥ 7,3

À partir de C # 7.3 (disponible avec Visual Studio 2017 ≥ v15.7), ce code est maintenant complètement valide:

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

C # ≤ 7,2

Vous pouvez avoir une contrainte d'enum imposée par le compilateur réel en abusant de l'héritage de contrainte. Le code suivant spécifie à la fois une contrainte class et une contrainte 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>
{
}

Usage:

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

Remarque: ceci est spécifiquement indiqué dans la spécification de langage C # 5.0:

Si le paramètre de type S dépend du paramètre de type T, alors: [...] Il est valide que S ait la contrainte de type valeur et que T ait la contrainte de type référence. Effectivement, cela limite T aux types System.Object, System.ValueType, System.Enum et à tout type d'interface.

192
Julien Lebosquain

Modifier

La question a maintenant été superbement répondue par Julien Lebosquain . J'aimerais également étendre sa réponse avec ignoreCase, defaultValue et des arguments optionnels, tout en ajoutant TryParse et 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
}

Exemples d'utilisation:

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

Ancien

Mes anciennes améliorations sur réponse de Vivek en utilisant les commentaires et les "nouveaux" développements:

  • utilisez TEnum pour plus de clarté pour les utilisateurs
  • ajouter plus de contraintes d'interface pour une vérification supplémentaire des contraintes
  • let TryParse manipule ignoreCase avec le paramètre existant (introduit dans VS2010/.Net 4)
  • éventuellement utiliser le générique valeur default (introduit dans VS2005/.Net 2)
  • use arguments facultatifs (introduit dans VS2010/.Net 4) avec les valeurs par défaut, pour defaultValue et ignoreCase

résultant en:

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

Vous pouvez définir un constructeur statique pour la classe qui vérifiera que le type T est une énumération et lève une exception s'il ne l'est pas. C'est la méthode mentionnée par Jeffery Richter dans son livre CLR via C #.

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

Ensuite, dans la méthode d'analyse, vous pouvez simplement utiliser Enum.Parse (typeof (T), entrée, true) pour convertir une chaîne en une énumération. Le dernier paramètre vrai sert à ignorer la casse de l'entrée.

18
Karg

Il convient également de noter que, depuis la publication de C # 7.3 en utilisant les contraintes Enum, elle est prise en charge immédiatement, sans avoir à effectuer de vérification supplémentaire.

Alors, si vous avez changé la version linguistique de votre projet en C # 7.3, le code suivant fonctionnera parfaitement:

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

Si vous ne savez pas comment changer de version linguistique en C # 7.3, reportez-vous à la capture d'écran suivante: enter image description here

EDIT 1 - Version requise de Visual Studio et prise en compte de ReSharper

Pour que Visual Studio reconnaisse la nouvelle syntaxe, vous devez avoir au minimum la version 15.7. Vous pouvez trouver cela également mentionné dans les notes de publication de Microsoft, voir Notes de publication de Visual Studio 2017 15.7 . Merci @MohamedElshawaf pour avoir souligné cette question valide.

Veuillez noter que dans mon cas, ReSharper 2018.1 au moment de la rédaction de cet EDIT ne prend pas encore en charge le format C # 7.3. Ayant activé ReSharper, il met en évidence la contrainte Enum comme une erreur indiquant que ne peut pas utiliser 'System.Array', 'System.Delegate', 'System.Enum', 'System.ValueType', 'object' comme type paramètre contrainte . ReSharper suggère une solution rapide à Supprimer la contrainte 'Enum' du type paramètre T de la méthode

Cependant, si vous désactivez temporairement ReSharper sous Outils -> Options -> ReSharper Ultimate -> Général , vous constaterez que la syntaxe est parfaitement correcte étant donné que vous utilisez VS 15.7 ou une version ultérieure. et C # 7.3 ou supérieur.

15
baumgarb

J'ai modifié l'échantillon par dimarzionist. Cette version ne fonctionnera qu'avec Enums et ne laissera pas passer les structures.

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

J'ai essayé d'améliorer un peu le code:

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

J'ai une exigence spécifique où je devais utiliser enum avec du texte associé à la valeur enum. Par exemple, lorsque j'utilise enum pour spécifier le type d'erreur, il est nécessaire de décrire les détails de l'erreur.

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

J'espère que cela vous sera 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

Ceci est ma prise en compte. Combiné à partir des réponses et 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));
    }
}

source MSDN

3
KarmaEDV

Fait intéressant, apparemment, il s’agit de possible dans d’autres langues (géré directement en C++, IL).

Citer:

... Les deux contraintes produisent en réalité un IL valide et peuvent également être utilisées par C # si elles sont écrites dans un autre langage (vous pouvez déclarer ces contraintes en C++ géré ou en IL).

Qui sait

3
Andrew Backer

Les réponses existantes sont vraies à partir de C # <= 7.2. Cependant, il existe un langage C # demande de fonctionnalité (lié à une demande corefx fonctionnalité) permettant les opérations suivantes;

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

Au moment de la rédaction de cet article, la fonctionnalité est "En discussion" lors des réunions de développement linguistique.

EDIT

Selon l'information de nawfal , elle est introduite en C # 7. .

3
DiskJunky

J'ai encapsulé la solution de Vivek dans une classe utilitaire que vous pouvez réutiliser. Veuillez noter que vous devez toujours définir des contraintes de type "où T: struct, IConvertible" sur votre type.

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

J'ai toujours aimé ceci (vous pouvez le modifier si nécessaire):

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

Comme indiqué dans d'autres réponses avant; bien que cela ne puisse pas être exprimé en code source, cela peut être fait au niveau IL. @Christopher Currens answer montre comment l'IL y parvient.

Avec Fody s Add-In ExtraConstraints.Fody , il existe un moyen très simple, complet avec un outil de construction, pour y parvenir. Ajoutez simplement leurs paquets de nugets (Fody, ExtraConstraints.Fody) à votre projet et ajoutez les contraintes comme suit (extrait du fichier Lisezmoi d’ExtrealConstraints):

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

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

et Fody ajoutera l'IL nécessaire à la présence de la contrainte. Notez également la fonctionnalité supplémentaire qui consiste à contraindre les délégués:

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

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

En ce qui concerne Enums, vous pouvez également prendre note du très intéressant Enums.NET .

1
BatteryBackupUnit

J'ai créé une extension Méthode to get integer value from enum regarde la mise en oeuvre de la méthode

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

c'est usage

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

J'ai adoré la solution de Christopher Currens en utilisant IL, mais pour ceux qui ne veulent pas s'occuper de la tâche complexe d'inclure MSIL dans leur processus de construction, j'ai écrit une fonction similaire en C #.

Veuillez noter cependant que vous ne pouvez pas utiliser de restriction générique comme where T : Enum parce qu'Enum est un type spécial. Par conséquent, je dois vérifier si le type générique donné est vraiment enum.

Ma fonction est:

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

Si vous pouvez utiliser la diffusion directe ultérieurement, vous pouvez utiliser la classe de base System.Enum dans votre méthode, le cas échéant. Il vous suffit de remplacer les paramètres de type avec précaution. Donc, la mise en œuvre de la méthode serait comme:

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

Ensuite, vous pouvez l'utiliser comme:

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