Je construis une fonction pour étendre le Enum.Parse
concept
- Permet d'analyser une valeur par défaut au cas où aucune valeur Enum ne serait trouvée
- Est insensible à la casse
J'ai donc é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
.
D'accord, mais existe-t-il une solution de contournement pour autoriser une énumération générique, ou vais-je devoir imiter la Parse
fonction et passer un type en tant qu'attribut, ce qui force l'exigence de boxe laide à votre code.
EDIT Toutes les suggestions ci-dessous ont été grandement appréciées, merci.
Je me suis installé (j'ai quitté 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é une solution générique de type sécurisé imposée par le compilateur dans MSIL ou F # ci-dessous, qui vaut bien un coup d'œil, et un vote positif. Je supprimerai cette modification si la solution bouillonne plus haut sur la page.
Réponses:
Puisque
Enum
Type implémente l'IConvertible
interface, une meilleure implémentation devrait ressembler à ceci:Cela permettra toujours le passage des types de valeurs implémentés
IConvertible
. Les chances sont cependant rares.la source
Cette fonctionnalité est enfin prise en charge en C # 7.3!
L'extrait de code suivant (à partir des exemples dotnet ) montre comment:
Veillez à définir votre version de langue dans votre projet C # sur la version 7.3.
Réponse originale ci-dessous:
Je suis en retard dans le match, mais je l'ai pris comme un défi pour voir comment cela pourrait être fait. Ce n'est pas possible en C # (ou VB.NET, mais faites défiler vers le bas pour F #), mais c'est possible dans MSIL. J'ai écrit cette petite chose ....
Ce qui génère une fonction qui serait ressembler à cela, si elle était valide C #:
Puis avec le code C # suivant:
Malheureusement, cela signifie que cette partie de votre code est écrite en MSIL au lieu de C #, le seul avantage supplémentaire étant que vous êtes en mesure de contraindre cette méthode
System.Enum
. C'est aussi une sorte de déception, car il est compilé dans un assemblage séparé. 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:vous obtenez un netmodule au lieu d'un assemblage.
Malheureusement, VS2010 (et plus tôt, évidemment) ne prend pas en charge l'ajout de références de netmodule, ce qui signifie que vous devez le laisser dans 2 assemblys distincts lorsque vous déboguez. La seule façon de les ajouter dans le cadre de votre assembly serait d'exécuter vous-même csc.exe à l'aide de l'
/addmodule:{files}
argument de ligne de commande. Ce ne serait pas trop douloureux dans un script MSBuild. Bien sûr, si vous êtes courageux ou stupide, vous pouvez exécuter manuellement csc à chaque fois. Et cela devient certainement plus compliqué car plusieurs assemblages doivent y avoir accès.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.
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 #.Celui-ci est plus facile à maintenir 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 pour cela. Cependant, il produit naturellement un IL considérablement différent (le code est très différent) et il dépend de la
FSharp.Core
bibliothèque, 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 des structures autrement synonymes:
la source
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.
T
àSystem.Enum
ne serait pas en mesure de faire toutes les choses avecT
que les gens pourraient attendre, les auteurs de C # cernées ils peuvent aussi bien interdire complètement. Je considère la décision malheureuse, car C # ayant simplement ignoré tout traitement spécial desSystem.Enum
contraintes, il aurait été possible d'écrire uneHasAnyFlags<T>(this T it, T other)
méthode d'extension qui était des ordres de grandeur plus rapide queEnum.HasFlag(Enum)
et qui vérifiait ses arguments par type.C # ≥ 7,3
À partir de C # 7.3 (disponible avec Visual Studio 2017 ≥ v15.7), ce code est maintenant complètement valide:
C # ≤ 7,2
Vous pouvez avoir une véritable contrainte d'énumération imposée par le compilateur en abusant de l'héritage des contraintes. Le code suivant spécifie à la fois les contraintes a
class
et astruct
:Usage:
Remarque: cela est spécifiquement indiqué dans la spécification du langage C # 5.0:
la source
EnumClassUtils<System.Enum>
est suffisant pour restreindre T à toutSystem.Enum
type dérivé.struct
on leParse
restreint ensuite à un type d'énumération réel. Vous devez vous limiterEnum
à un moment donné. Pour ce faire, votre classe doit être imbriquée. Voir gist.github.com/MrJul/7da12f5f2d6c69f03d79where TClass : class
contrainte gagne ici?enum DefinitelyNotAnInt : byte { Realize, That, I, Am, Not, An, Int }
enum AlsoNotAnInt : long { Well, Bummer }
Éditer
Julien Lebosquain a superbement répondu à la question . Je voudrais également étendre sa réponse avec
ignoreCase
,defaultValue
et les arguments facultatifs, tout en ajoutantTryParse
etParseOrDefault
.Exemples d'utilisation:
Vieux
Mes anciennes améliorations sur la réponse de Vivek en utilisant les commentaires et les «nouveaux» développements:
TEnum
pour plus de clarté pour les utilisateursTryParse
gérerignoreCase
avec le paramètre existant (introduit dans VS2010 / .Net 4)default
valeur générique (introduite dans VS2005 / .Net 2)defaultValue
etignoreCase
résultant en:
la source
Vous pouvez définir un constructeur statique pour la classe qui vérifiera que le type T est une énumération et lèvera une exception s'il ne l'est pas. C'est la méthode mentionnée par Jeffery Richter dans son livre CLR via C #.
Ensuite, dans la méthode d'analyse, vous pouvez simplement utiliser Enum.Parse (typeof (T), input, true) pour convertir une chaîne en énumération. Le dernier vrai paramètre est pour ignorer la casse de l'entrée.
la source
Enum
T
lorsque le constructeur a exécuté. Bien que ce soit bien plus agréable que d'attendre un constructeur d'instance.Il convient également de tenir compte du fait que depuis la sortie de C # 7.3 à l'aide des contraintes Enum est prise en charge hors de la boîte sans avoir à faire de vérification supplémentaire et d'autres choses.
Donc, à l'avenir et étant donné que vous avez changé la version linguistique de votre projet en C # 7.3, le code suivant fonctionnera parfaitement:
Dans le cas où vous ne savez pas comment changer la version linguistique en C # 7.3, voir la capture d'écran suivante:
EDIT 1 - Version Visual Studio requise et prise en compte de ReSharper
Pour que Visual Studio reconnaisse la nouvelle syntaxe, vous avez besoin au moins de 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 d'avoir signalé cette question valide.
Veuillez également noter que dans mon cas, ReSharper 2018.1 au moment de la rédaction de cet EDIT ne prend pas encore en charge C # 7.3. L'activation de ReSharper met en surbrillance la contrainte Enum comme une erreur m'indiquant Ne peut pas utiliser 'System.Array', 'System.Delegate', 'System.Enum', 'System.ValueType', 'object' comme contrainte de paramètre de type . ReSharper suggère comme solution rapide pour supprimer la contrainte «Enum» du type de paramètre T de la méthode
Cependant, si vous désactivez temporairement ReSharper sous Outils -> Options -> ReSharper Ultimate -> Général, vous verrez que la syntaxe est parfaitement correcte étant donné que vous utilisez VS 15.7 ou supérieur et C # 7.3 ou supérieur.
la source
where T : struct, Enum
, pour éviter de se passerSystem.Enum
comme paramètre de type.struct, Enum
. Ma justification est expliquée dans la réponse et les commentaires ici .J'ai modifié l'échantillon par dimarzionist. Cette version ne fonctionnera qu'avec Enums et ne laissera pas passer les structures.
la source
J'ai essayé d'améliorer un peu le code:
la source
defaultValue.ToString("D", System.Globalization.NumberFormatInfo.CurrentInfo)
même si vous ne savez pas de quel type d'énumération il s'agit, seulement que l'objet est une énumération.IsDefined
ruinera cependant l'insensibilité à la casse. Contrairement àParse
,IsDefined
n'a aucunignoreCase
argument et MSDN indique qu'il ne correspond qu'à la casse exacte .J'ai une exigence spécifique où j'ai besoin d'utiliser enum avec du texte associé à la valeur enum. Par exemple, lorsque j'utilise enum pour spécifier le type d'erreur, il fallait décrire les détails de l'erreur.
la source
J'espère que cela vous sera utile:
la source
return (TValue)Enum.Parse(typeof (TValue), value);
parreturn (TValue)Enum.Parse(typeof (TValue), value, true);
Chose intéressante, apparemment, cela est possible dans d'autres langages (Managed C ++, IL directement).
Citer:
Qui sait
la source
C'est mon point de vue. Combiné à partir des réponses et MSDN
Source MSDN
la source
TEnum
s'agit en fait d'un type Enum mais qu'iltext
s'agit d'une chaîne vide, vous obtenez unArgumentException
dicton "TEnum doit être un type Enum" même s'il l'est.Les réponses existantes sont vraies à partir de C # <= 7.2. Cependant, il existe une demande de fonctionnalité en langage C # (liée à une demande de fonctionnalité corefx ) pour permettre ce qui suit;
Au moment de la rédaction du présent article, la fonctionnalité était "En discussion" lors des réunions de développement linguistique.
ÉDITER
Selon les informations de nawfal , cela est introduit dans C # 7.3 .
la source
J'ai toujours aimé cela (vous pouvez le modifier selon vos besoins):
la source
J'ai adoré la solution de Christopher Currens utilisant IL, mais pour ceux qui ne veulent pas gérer des affaires délicates 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,
where T : Enum
car 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:
la source
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.
la source
J'ai créé une méthode d'extension,
to get integer value from enum
regardez la mise en œuvre de la méthodec'est l'usage
la source
Comme indiqué dans d'autres réponses auparavant; bien que cela ne puisse pas être exprimé en code source, cela peut en fait ê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 des outils de construction, pour y parvenir. Ajoutez simplement leurs packages nuget (
Fody
,ExtraConstraints.Fody
) à votre projet et ajoutez les contraintes comme suit (extrait du fichier Lisez-moi des contraintes supplémentaires):et Fody ajoutera l'IL nécessaire pour que la contrainte soit présente. Notez également la fonction supplémentaire de contraindre les délégués:
En ce qui concerne Enums, vous voudrez peut-être également prendre note du très intéressant Enums.NET .
la source
Ceci est ma mise en œuvre. Fondamentalement, vous pouvez configurer n'importe quel attribut et cela fonctionne.
la source
Si vous pouvez utiliser la conversion directe par la suite, je suppose que vous pouvez utiliser la
System.Enum
classe de base dans votre méthode, si nécessaire. Il vous suffit de remplacer soigneusement les paramètres de type. Ainsi, l'implémentation de la méthode serait comme:Ensuite, vous pouvez l'utiliser comme:
la source
Enum.ToObject()
donnerait un résultat plus flexible. Ajouté à cela, vous pouvez faire des comparaisons de chaînes sans respecter la casse, ce qui annulerait la nécessité d'appelerToLower()
Juste pour être complet, ce qui suit est une solution Java. Je suis certain que la même chose pourrait être faite en C # également. Cela évite d'avoir à spécifier le type n'importe où dans le code - à la place, vous le spécifiez dans les chaînes que vous essayez d'analyser.
Le problème est qu'il n'y a aucun moyen de savoir à quelle énumération la chaîne peut correspondre - la réponse est donc de résoudre ce problème.
Au lieu d'accepter uniquement la valeur de chaîne, acceptez une chaîne qui possède à la fois l'énumération et la valeur sous la forme "enumeration.value". Le code de travail est ci-dessous - nécessite Java 1.8 ou version ultérieure. Cela rendrait également le XML plus précis, car vous verriez quelque chose comme color = "Color.red" au lieu de simplement color = "red".
Vous appelez la méthode acceptEnumeratedValue () avec une chaîne contenant le nom de la valeur du point du nom enum.
La méthode renvoie la valeur énumérée formelle.
la source