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

je construis une fonction pour étendre le Enum.Parse concept que

  • permet d'analyser une valeur par défaut dans le cas où une valeur Enum n'est pas trouvée
  • Est pas sensible à la casse

donc 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 suis une Erreur de la Contrainte ne peut pas être classe spéciale System.Enum .

juste assez, mais y a-t-il une solution pour permettre à un Enum générique, ou est-ce que je vais devoir imiter la fonction Parse et passer un type comme un attribut, ce qui force l'exigence de boxe laid à votre code.

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

Ont réglé sur (j'ai laissé la boucle pour maintenir le compte de la casse - j'utilise ce lors de l'analyse 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évr. 2015) Julien Lebosquain a récemment publié une solution générique de type de compilateur forcé dans MSIL ou F# ci-dessous, ce qui vaut bien un regard, et un upvote. Je vais supprimer cette édition si la solution bulles plus haut de la page.

969
demandé sur Vadim Ovchinnikov 2008-09-17 05:56:19

20 réponses

depuis Enum type implements IConvertible interface, une meilleure implémentation devrait être quelque chose comme 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 la transmission de types de valeurs mettant en œuvre IConvertible . Les chances sont rares.

873
répondu Vivek 2016-07-21 09:34:16

cette fonctionnalité est finalement supportée en C # 7.3!

l'extrait suivant (tiré de the dotnet samples ) démontre son utilisation:

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

assurez-vous de mettre votre version linguistique dans votre projet C# à la version 7.3.


réponse originale ci-dessous:

je suis en retard au match, mais je l'ai pris comme un défi pour voir comment il pourrait être fait. C'est pas possible en C# (ou VB.NET, mais faites défiler vers le bas pour F#), mais est possible dans MSIL. J'ai écrit ce petit....thing

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

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 avoir cette partie de votre code écrit en MSIL au lieu de C#, avec le seul avantage supplémentaire est que vous pouvez limiter cette méthode par System.Enum . C'est aussi un problème, car il est compilé dans une assemblée distincte. Cependant, cela ne signifie pas que vous devez le déployer de cette façon.

en supprimant la ligne .assembly MyThing{} et en invoquant ilasm comme suit:

ilasm.exe /DLL /OUTPUT=MyThing.netmodule

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

malheureusement, VS2010 (et plus tôt, évidemment) ne supportez l'ajout de références netmodule, ce qui signifie que vous devez le laisser dans 2 assemblages séparés lorsque vous déboguez. La seule façon de les ajouter à votre assemblée serait de gérer le csc.exe vous-même en utilisant l'argument de ligne de commande /addmodule:{files} . Ce ne serait pas trop douloureux dans un script MSBuild. Bien sûr, si vous êtes courageux ou stupide, vous pouvez exécuter le scc vous-même manuellement à chaque fois. Et cela devient certainement plus compliqué car plusieurs assemblages ont besoin d'accéder à il.

ainsi, il peut être fait dans .Net. Cela vaut-il la peine de faire un effort supplémentaire? Euh, eh bien, je suppose que je vais vous laisser décider.


F # Solution as alternative

le Crédit Supplémentaire: Il s'avère qu'un générique de restriction sur enum est possible dans au moins un autre .NET langue en dehors de l'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

C'est plus facile à maintenir car c'est bien connu langage avec support IDE Visual Studio complet, mais vous avez encore besoin d'un projet séparé dans votre solution. Cependant, il produit naturellement Des IL très différents (le code est très différent) et il s'appuie 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 que la solution MSIL), et pour montrer qu'il échoue correctement sur autrement 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);
439
répondu Christopher Currens 2018-05-12 20:23:26

c# ≥ 7.3

commençant par C# 7.3 (disponible avec Visual Studio 2017 ≥ v15.7), ce code est maintenant entièrement valide:

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

c # ≤ 7,2

vous pouvez avoir un vrai compilateur contrainte enum forcée en abusant de l'héritage de contrainte. Le code suivant spécifie à la fois une class et une struct contraintes en même temps:

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

Note: Ceci est précisé dans la spécification linguistique C# 5.0:

si le paramètre de type S dépend du paramètre de type T alors: [...] Il est valable pour S pour avoir la valeur de contrainte de type T pour avoir le type de référence contrainte. Dans les faits, cela limite le système des types.Objet, Système.ValueType, System.Enum, et tout type d'interface.

155
répondu Julien Lebosquain 2018-05-14 08:39:21

Modifier

la réponse à cette question a été superbement donnée par Julien Lebosquain . Je voudrais é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

Mon vieux améliorations sur Vivek la réponse de en utilisant les commentaires et les "nouveaux" développements:

  • utiliser TEnum pour plus de clarté pour les utilisateurs
  • ajouter plus d'interface-contraintes pour contrainte supplémentaire-vérification
  • let TryParse poignée ignoreCase avec le paramètre (introduit dans VS2010/. Net 4)
  • utilisation facultative le générique default valeur (introduit dans VS2005/.Net 2)
  • utiliser arguments optionnels (introduit dans VS2010 / .Net 4) avec valeurs par défaut, pour defaultValue et 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;
    }
}
29
répondu Yahoo Serious 2017-05-23 11:55:09

vous pouvez définir un constructeur statique pour la classe qui vérifiera que le type T est un enum et jeter une exception si ce n'est pas le cas. 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");
        }
    }
}

alors dans la méthode parse, vous pouvez juste utiliser Enum.Parse(typeof (T), input, true) pour passer de string à enum. Le dernier vrai paramètre est ignoré cas de l'entrée.

18
répondu Karg 2008-09-17 15:38:07

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
répondu Bivoauc 2012-04-30 23:42:54

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
répondu Martin 2013-05-07 04:12:19

il faut aussi tenir compte du fait que la sortie de C# 7.3 en utilisant les contraintes Enum est supportée sans avoir à procéder à des vérifications supplémentaires.

donc à l'avenir et étant donné que vous avez changé la version linguistique de votre projet en C # 7.3, le code suivant va parfaitement fonctionner:

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

dans le cas où vous ne savez pas comment changer la version de langue en C # 7.3 voir la capture d'écran suivante: enter image description here

Modifier 1 - Version Studio visuelle requise et envisager de reformuler

pour Visual Studio pour reconnaître la nouvelle syntaxe, vous avez besoin au moins de la version 15.7. Vous pouvez trouver cela également mentionné dans les notes de version de Microsoft, voir Visual Studio 2017 15.7 notes de version . Merci @MohamedElshawaf d'avoir souligné cette question valable.

Pls aussi noter que dans mon cas ReSharper 2018.1 au moment de la rédaction de cette édition ne supporte pas encore C# 7.3. Ayant ReSharper activé il met en évidence la contrainte Enum comme une erreur me disant ne peut pas utiliser le système.Tableau', ' Système.Délégué', 'Système.Enum', ' System.ValueType', 'objet' comme paramètre de type de contrainte . ReSharper suggère comme une solution rapide à supprimer la contrainte "Enum" du type de paramètre T de la méthode

Cependant, si vous désactivez temporairement ReSharper sous Tools -> Options -> ReSharper Ultimate -> General vous verrez que la syntaxe est parfaitement correcte étant donné que vous utilisez VS 15.7 ou plus et C# 7.3 ou plus.

7
répondu baumgarb 2018-05-12 17:04:35

j'ai besoin spécifique où je obligé d'utiliser enum avec le texte associé à valeur d'enum. Par exemple, lorsque j'utilise enum pour spécifier le type d'erreur requis pour 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
répondu Sunny Rajwadi 2010-04-18 05:38:20

j'Espère que cela est 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
répondu dimarzionist 2013-05-07 04:11:50

assez intéressant, apparemment c'est possible dans d'autres langauges (géré C++, IL directement).

, Pour Citer:

... Les deux contraintes produisent en fait un IL valide et peuvent aussi être consommées par C# si elles sont écrites dans une autre langue (vous pouvez déclarer ces contraintes dans managed C++ ou dans IL).

qui sait

3
répondu Andrew Backer 2009-07-07 17:00:47

c'est mon avis. Combiné des réponses et du 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 Source

3
répondu KarmaEDV 2014-03-25 16:05:51

Les réponses sont vrai que de C# <=7.2. Toutefois, il existe un langage C# requête relative à une caractéristique (lié à un corefx requête relative à une caractéristique) pour permettre ce qui suit:

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

au moment de la rédaction, la caractéristique est" en discussion " aux réunions sur le développement linguistique.

MODIFIER

Que par nawfal info, c'est introduit en C# 7.3 .

3
répondu DiskJunky 2018-04-12 20:35:52

j'ai toujours aimé ceci (vous pouvez 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
répondu Jeff 2010-10-05 20:04:34

j'ai adoré la solution de Christopher Currens en utilisant IL mais pour ceux qui ne veulent pas faire face à l'entreprise délicate de L'inclusion de MSIL dans leur processus de construction, j'ai écrit la fonction similaire dans C#.

s'il vous plaît noter que vous ne pouvez pas utiliser la restriction générique comme where T : Enum parce que L'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
répondu expert 2012-04-23 01:23:29

J'ai encapsulé la solution de Vivek dans une classe utilitaire que vous pouvez réutiliser. Veuillez noter que vous devez encore définir les 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
répondu niaher 2013-09-24 07:42:30

j'ai créé une méthode d'extension to get integer value from enum jeter un regard à la méthode de la mise en œuvre

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 l'usage

MemberStatusEnum.Activated.ToInt()// using extension Method
(int) MemberStatusEnum.Activated //the ordinary way
1
répondu Basheer AL-MOMANI 2016-11-01 10:22:43

comme indiqué dans d'autres réponses avant; bien que cela ne puisse pas être exprimé dans le code source, il peut effectivement être fait au niveau IL. @Christopher Currens answer montre comment IL fait à cela.

avec Fody s Add-In Extracontraints.Fody il y a un moyen très simple, avec des outils de construction, pour atteindre cet objectif. Il suffit d'ajouter leurs paquets nuget ( Fody , ExtraConstraints.Fody ) à votre projet et d'ajouter le contraintes comme suit (extrait du Readme of ExtraConstraints):

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

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

et Fody ajoutera L'IL nécessaire pour que la contrainte soit présente. Notons également la caractéristique supplémentaire de 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 pourriez également prendre note du très intéressant Enums.NET .

1
répondu BatteryBackupUnit 2017-07-24 06:49:10

S'il est correct d'utiliser le moulage direct après coup, je suppose que vous pouvez utiliser la classe de base System.Enum dans votre méthode, si nécessaire. Vous avez juste besoin de remplacer les paramètres de type soigneusement. Ainsi, la méthode de mise en œuvre 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;
    }
}

, Alors vous pouvez l'utiliser comme:

var parsedOutput = (YourEnum)EnumUtils.GetEnumFromString(someString, YourEnum.DefaultValue);
0
répondu uluorta 2017-11-23 14:39:37

en java, vous utiliseriez...

    SomeClass<T extends enum> {
}

assez simple, ça.

-3
répondu Rodney P. Barbati 2018-05-30 04:11:57