Association d'énumérations à des chaînes en C #

363

Je sais que ce qui suit n'est pas possible car le type de l'énumération doit être un int

enum GroupTypes
{
    TheGroup = "OEM",
    TheOtherGroup = "CMB"
}

De ma base de données, j'obtiens un champ avec des codes incompréhensibles (les OEMet CMBles). Je voudrais faire de ce champ un enumou quelque chose d'autre compréhensible. Parce que si la cible est la lisibilité, la solution doit être concise.

Quelles autres options ai-je?

Boris Callens
la source
doublon possible de Enum ToString
nawfal
12
Je ne sais pas pourquoi la plupart des réponses n'utilisent pas seulement "const string" et au lieu de cela, elles créent des classes personnalisées.
CTS_AE
1
Vous ne pourrez peut-être pas utiliser de chaînes, mais vous pouvez très bien utiliser des caractères. C'est une option si vous pouvez utiliser des valeurs à une seule lettre.
T. Sar
1
Vraiment confus quant à la raison pour laquelle la solution proposée ci-dessus par CTS_AE n'est même pas dans les trois premières réponses.
Sinjai
@Sinjai Le regroupement explicite des valeurs associées l'emportera sur la pénalité d'une perte de performance imperceptible, en particulier dans une API ou un composant réutilisable.
person27

Réponses:

404

J'aime utiliser des propriétés dans une classe au lieu de méthodes, car elles ressemblent plus à des énumérations.

Voici un exemple pour un enregistreur:

public class LogCategory
{
    private LogCategory(string value) { Value = value; }

    public string Value { get; set; }

    public static LogCategory Trace   { get { return new LogCategory("Trace"); } }
    public static LogCategory Debug   { get { return new LogCategory("Debug"); } }
    public static LogCategory Info    { get { return new LogCategory("Info"); } }
    public static LogCategory Warning { get { return new LogCategory("Warning"); } }
    public static LogCategory Error   { get { return new LogCategory("Error"); } }
}

Transmettez des valeurs de chaîne sécurisées en tant que paramètre:

public static void Write(string message, LogCategory logCategory)
{
    var log = new LogEntry { Message = message };
    Logger.Write(log, logCategory.Value);
}

Usage:

Logger.Write("This is almost like an enum.", LogCategory.Info);
Même Mien
la source
4
Le seul inconvénient que je peux trouver, c'est que ce serait un peu plus lent, mais dans la plupart des cas, ce serait négligeable. Et il n'aurait pas exactement le même comportement dans l'éditeur. EG: basculer sur celui-ci ne remplirait pas automatiquement un cas pour chaque possibilité. Outre ces points mineurs, je pense que c'est probablement une solution assez simple.
Boris Callens
3
Et il est facile d'utiliser Dictionary <LogCategory, Action / Func> comme commutateur. :)
Arnis Lapsa
4
@ArnisL. Il ne suffit pas de travailler en tant que clé, vous devez remplacer Equals () et GetHashCode (), et vous voulez rendre le configurateur de propriétés Value privé. Pourtant, ce n'est pas une énumération.
Dave Van den Eynde
21
Pour mon usage personnel, j'ai développé ce concept, en remplaçant la ToStringméthode à retourner Value. Et ensuite fourni des opérateurs de transtypage implicites vers et depuis une chaîne. public static implicit operator String(LogCategory category) { return Value; }.
Zarepheth
6
Qu'en est-il de l'utilisation dans des boîtiers de commutation?
David
176

Vous pouvez également utiliser le modèle d'extension:

public enum MyEnum
{
    [Description("String 1")]
    V1= 1,
    [Description("String 2")]
    V2= 2
} 

Votre classe d'extension

public static class MyEnumExtensions
{
    public static string ToDescriptionString(this MyEnum val)
    {
        DescriptionAttribute[] attributes = (DescriptionAttribute[])val
           .GetType()
           .GetField(val.ToString())
           .GetCustomAttributes(typeof(DescriptionAttribute), false);
        return attributes.Length > 0 ? attributes[0].Description : string.Empty;
    }
} 

usage:

MyEnum myLocal = MyEnum.V1;
print(myLocal.ToDescriptionString());
Glennular
la source
3
Voir aussi stackoverflow.com/questions/4367723/… pour une autre extension et de la chaîne à l'énumération à titre de description.
Dave
15
Je ne peux pas m'empêcher de penser que le fait de refléter l'énumération à chaque fois que vous affichez le texte semble un peu douloureux du point de vue de la performance!
Liath
4
@Liath - Le `.ToString ()` utilise déjà la réflexion, donc vous ne perdez vraiment rien avec cette approche et gagnez en lisibilité
James King
1
Pourriez-vous rendre l'extension générique afin qu'elle s'applique automatiquement à toutes les énumérations?
erosebe
3
Pour rendre générique, utilisez public static string ToDescriptionString(this Enum ...ie sans taper explicitement à MyEnum.
LeeCambl
100

Que diriez-vous d'utiliser une classe statique avec des constantes?

static class GroupTypes
{
  public const string TheGroup = "OEM";
  public const string TheOtherGroup = "CMB";
}

void DoSomething(string groupType)
{
  if(groupType == GroupTypes.TheGroup)
  {
    // Be nice
  }  
  else if (groupType == GroupTypes.TheOtherGroup)
  {
    // Continue to be nice
  }
  else
  {
    // unexpected, throw exception?
  }
}
rpattabi
la source
9
D'accord. J'ai du mal à voir le but derrière les solutions plus complexes, sauf peut-être pour pouvoir basculer sur l'énumération résultante.
fakeleft
@fakeleft vous ne pouvez pas utiliser un type de classe statique avec un type générique (modèle), et peut-être d'autres limitations, je pense que c'est pourquoi les gens préfèrent les solutions "plus complexes".
eselk
2
Les constantes doivent être internes ou publiques pour que cela fonctionne
arviman
46
Les types statiques ne peuvent pas être utilisés comme paramètres.
Pedro Moreira
2
Comme le souligne @PedroMoreira, vous ne pouvez pas passer GroupTypescomme type d'argument car c'est une classe statique. C'est le problème que la réponse d'Even Mien résout. Dans ce cas, vous devriez plutôt avoir void DoSomething(string groupType), ce qui signifie alors que cela groupTypepourrait avoir n'importe quelle valeur de chaîne , même des valeurs que vous n'attendez pas, ce qui signifie que vous devez être préparé pour ces types non valides et décider comment les gérer (par exemple en lançant une exception). Même la réponse de Mien résout cela en limitant le nombre d'entrées valides aux options définies par la LogCategoryclasse.
Pharap
30

Vous pouvez ajouter des attributs aux éléments de l'énumération, puis utiliser la réflexion pour obtenir les valeurs des attributs.

Vous devez utiliser le spécificateur "field" pour appliquer les attributs, comme ceci:

enum GroupTypes
{
    [field:Description("OEM")]
    TheGroup,

    [field:Description("CMB")]
    TheOtherGroup
}

Vous réfléchissez ensuite aux champs statiques du type de l'énumération (dans ce cas, GroupTypes) et obtenez DescriptionAttributela valeur que vous recherchez à l'aide de la réflexion:

public static DescriptionAttribute GetEnumDescriptionAttribute<T>(
    this T value) where T : struct
{
    // The type of the enum, it will be reused.
    Type type = typeof(T);

    // If T is not an enum, get out.
    if (!type.IsEnum) 
        throw new InvalidOperationException(
            "The type parameter T must be an enum type.");

    // If the value isn't defined throw an exception.
    if (!Enum.IsDefined(type, value))
        throw new InvalidEnumArgumentException(
            "value", Convert.ToInt32(value), type);

    // Get the static field for the value.
    FieldInfo fi = type.GetField(value.ToString(), 
        BindingFlags.Static | BindingFlags.Public);

    // Get the description attribute, if there is one.
    return fi.GetCustomAttributes(typeof(DescriptionAttribute), true).
        Cast<DescriptionAttribute>().SingleOrDefault();
}

J'ai choisi de retourner le DescriptionAttributelui-même ci-dessus, dans le cas où vous souhaitez pouvoir déterminer si l'attribut est même appliqué.

casperOne
la source
Bien que je m'en souvienne pour des situations plus complexes, c'est plutôt complexe pour une situation avec le niveau de complexité de ce que j'ai déclaré dans le PO
Boris Callens
26

Vous pouvez le faire très facilement en fait. Utilisez le code suivant.

enum GroupTypes
{
   OEM,
   CMB
};

Ensuite, lorsque vous souhaitez obtenir la valeur de chaîne de chaque élément enum, utilisez simplement la ligne de code suivante.

String oemString = Enum.GetName(typeof(GroupTypes), GroupTypes.OEM);

J'ai utilisé cette méthode avec succès dans le passé, et j'ai également utilisé une classe de constantes pour contenir des constantes de chaîne, les deux fonctionnent assez bien, mais j'ai tendance à préférer cela.

Arthur C
la source
Je pensais la même chose, mais il doit y avoir un problème ... Sinon, je soupçonnerais que plus de gens suggéreraient cela (peut-être que je suis juste paranoïaque).
Matthijs Wessels
Le seul problème que je connaisse est que je crois qu'il utilise la réflexion pour comprendre la chaîne. Par conséquent, si je suis juste à la recherche d'une solution pour garder une trace d'une chaîne constante, j'utilise généralement une classe pour stocker la majorité de mes chaînes constantes. Cependant, si j'ai une situation où un Enum est la bonne solution (indépendamment de l'obtention d'une chaîne descriptive sur mes éléments Enum), alors plutôt que d'avoir une chaîne supplémentaire flottant quelque part à gérer, j'utilise simplement la valeur enum comme décrit.
Arthur C
+1 C'est la réponse la meilleure et la plus simple, et elle a également des votes élevés ici pour le prouver. Le seul moment où il est préférable d'utiliser le modèle d'extension est lorsque vous avez besoin d'espaces dans le texte (plus de détails ici ).
SharpC
14
Non, il s'agit simplement d'obtenir le nom d'une valeur d'énumération, et non d'affecter une chaîne à une valeur d'énumération. Le but de l'OP est d'avoir une chaîne différente de la valeur énumérée, par exemple: TheGroup = "OEM", TheOtherGroup = "CMB".
Tim Autin
3
Je suis d'accord avec le commentaire de @ Tim, ce n'est pas ce que le PO essaie de faire. Si vous vous demandez ce qu'est un cas d'utilisation, envisagez une situation où un appareil prend des chaînes comme commandes, mais il doit également y avoir une version "lisible par l'homme" de la commande. J'en avais besoin pour associer quelque chose comme "Update Firmware" à la commande "UPDATEFW".
JYelton
20

Essayez d'ajouter des constantes à une classe statique. Vous ne vous retrouvez pas avec un type, mais vous aurez des constantes lisibles et organisées:

public static class GroupTypes {

    public const string TheGroup = "OEM";
    public const string TheOtherGroup = "CMB";

}
Darasd
la source
3
Difficile de revenir du code au nom descriptif. Vous devez utiliser la réflexion sur tous les champs const pour rechercher une correspondance.
andleer
1
@andleer Je ne comprends pas votre inquiétude. C'est la solution que j'utilise.
VSO
Oui, c'est précisément ce que je voulais. Et c'est la solution la plus concise / élégante que je vois, comme si je définissais une énumération avec des valeurs int - mais avec des valeurs de chaîne à la place. 100% parfait.
Chad
3
Le problème avec cela est qu'il ne fonctionne pas comme un Enum dans le sens où nous n'aurons pas de type séparé avec une liste finie de valeurs. Une fonction qui les attendrait pourrait être utilisée avec des chaînes de forme libre sujettes aux erreurs.
Juan Martinez
14

Créez une deuxième énumération pour votre base de données contenant les éléments suivants:

enum DBGroupTypes
{
    OEM = 0,
    CMB = 1
}

Maintenant, vous pouvez utiliser Enum.Parse pour récupérer la valeur DBGroupTypes correcte à partir des chaînes "OEM" et "CMB". Vous pouvez ensuite les convertir en int et récupérer les valeurs correctes à partir de la bonne énumération que vous souhaitez utiliser davantage dans votre modèle.

Dave Van den Eynde
la source
Cela semble être une étape supplémentaire dans le processus, pourquoi pas une classe qui gère tout?
C. Ross
11
Par opposition à l'utilisation d'attributs et de réflexion?
Dave Van den Eynde
13

Utilisez une classe.

Edit: Meilleur exemple

class StarshipType
{
    private string _Name;
    private static List<StarshipType> _StarshipTypes = new List<StarshipType>();

    public static readonly StarshipType Ultralight = new StarshipType("Ultralight");
    public static readonly StarshipType Light = new StarshipType("Light");
    public static readonly StarshipType Mediumweight = new StarshipType("Mediumweight");
    public static readonly StarshipType Heavy = new StarshipType("Heavy");
    public static readonly StarshipType Superheavy = new StarshipType("Superheavy");

    public string Name
    {
        get { return _Name; }
        private set { _Name = value; }
    }

    public static IList<StarshipType> StarshipTypes
    {
        get { return _StarshipTypes; }
    }

    private StarshipType(string name, int systemRatio)
    {
        Name = name;
        _StarshipTypes.Add(this);
    }

    public static StarshipType Parse(string toParse)
    {
        foreach (StarshipType s in StarshipTypes)
        {
            if (toParse == s.Name)
                return s;
        }
        throw new FormatException("Could not parse string.");
    }
}
Traverser
la source
1
Difficile de revenir du code au nom descriptif. Vous devez utiliser la réflexion sur tous les champs const pour rechercher une correspondance.
andleer
1
Je vois ce que tu veux dire. Je vais télécharger une version qui fonctionne réellement plus tard, mais j'avoue que c'est assez lourd.
C. Ross
Ma version basée sur la solution de C. Ross stackoverflow.com/a/48441114/3862615
Roman M
7

Une autre façon de résoudre le problème est d'avoir une énumération et un tableau de chaînes qui mapperont les valeurs d'énumération avec la liste de chaînes:

public enum GroupTypes
{
    TheGroup  = 0,
    TheOtherGroup 
}

string[] GroupTypesStr = {
    "OEM",
    "CMB"
};

vous pouvez l'utiliser quelque chose comme ceci:

Log.Write(GroupTypesStr[(int)GroupTypes.TheOtherGroup]);

Cela invitera CMB

AVANTAGES:

  1. Code facile et propre.
  2. Haute performance (spécialement en comparaison avec les approches qui utilisent des classes)

LES INCONVÉNIENTS:

  1. Sujet à gâcher la liste lors de sa modification, mais ce sera bien pour une courte liste.
Luis Orantes
la source
6

Voici la méthode d'extension que j'ai utilisée pour obtenir la valeur d'énumération sous forme de chaîne. Voici d'abord l'énumération.

public enum DatabaseEnvironment
{
    [Description("AzamSharpBlogDevDatabase")]
    Development = 1, 
    [Description("AzamSharpBlogQADatabase")]
    QualityAssurance = 2, 
    [Description("AzamSharpBlogTestDatabase")] 
    Test = 3
}

L'attribut Description provient de System.ComponentModel.

Et voici ma méthode d'extension:

public static string GetValueAsString(this DatabaseEnvironment environment) 
{
    // get the field 
    var field = environment.GetType().GetField(environment.ToString());
    var customAttributes = field.GetCustomAttributes(typeof (DescriptionAttribute), false);

    if(customAttributes.Length > 0)
    {
        return (customAttributes[0] as DescriptionAttribute).Description;  
    }
    else
    {
        return environment.ToString(); 
    }
}

Maintenant, vous pouvez accéder à l'énumération en tant que valeur de chaîne à l'aide du code suivant:

[TestFixture]
public class when_getting_value_of_enum
{
    [Test]
    public void should_get_the_value_as_string()
    {
        Assert.AreEqual("AzamSharpBlogTestDatabase",DatabaseEnvironment.Test.GetValueAsString());  
    }
}
azamsharp
la source
5

Avez-vous envisagé une table de recherche à l'aide d'un dictionnaire?

enum GroupTypes
{
    TheGroup,
    TheOtherGroup
}

Dictionary<string, GroupTypes> GroupTypeLookup = new Dictionary<string, GroupTypes>();
// initialize lookup table:
GroupTypeLookup.Add("OEM", TheGroup);
GroupTypeLookup.Add("CMB", TheOtherGroup);

Vous pouvez ensuite utiliser GroupTypeLookup.TryGetValue () pour rechercher une chaîne lorsque vous la lisez.

Jim Mischel
la source
Comment obtenir facilement la clé d'une valeur donnée?
eglasius
La question ne demandait pas d'aller dans l'autre sens. Mais ce serait assez simple pour construire un autre dictionnaire qui va dans l'autre sens. C'est-à-dire, Dictionary <GroupTypes, string>.
Jim Mischel
4
public class DataType
{
    private readonly string value;
    private static readonly Dictionary<string, DataType> predefinedValues;

    public static readonly DataType Json = new DataType("json");
    public static readonly DataType Xml = new DataType("xml");
    public static readonly DataType Text = new DataType("text");
    public static readonly DataType Html = new DataType("html");
    public static readonly DataType Binary = new DataType("binary");

    static DataType()
    {
        predefinedValues = new Dictionary<string, DataType>();
        predefinedValues.Add(Json.Value, Json);
        predefinedValues.Add(Xml.Value, Xml);
        predefinedValues.Add(Text.Value, Text);
        predefinedValues.Add(Html.Value, Html);
        predefinedValues.Add(Binary.Value, Binary);
    }

    private DataType(string value)
    {
        this.value = value;
    }

    public static DataType Parse(string value)
    {
        var exception = new FormatException($"Invalid value for type {nameof(DataType)}");
        if (string.IsNullOrEmpty(value))
            throw exception;

        string key = value.ToLower();
        if (!predefinedValues.ContainsKey(key))
            throw exception;

        return predefinedValues[key];
    }

    public string Value
    {
        get { return value; }
    }
}
Roman M
la source
3

C # ne prend pas en charge les chaînes énumérées, mais pour la plupart des situations, vous pouvez utiliser une liste ou un dictionnaire pour obtenir l'effet souhaité.

Par exemple, pour imprimer les résultats de réussite / échec:

List<string> PassFail = new List<string> { "FAIL", "PASS" };
bool result = true;
Console.WriteLine("Test1: " + PassFail[result.GetHashCode()]);
user1441126
la source
2

Je voudrais en faire une classe et éviter complètement une énumération. Et puis, avec l'utilisation d'un gestionnaire de type, vous pouvez créer l'objet lorsque vous le récupérez dans la base de données.

C'EST À DIRE:

public class Group
{
    public string Value{ get; set; }
    public Group( string value ){ Value = value; } 
    public static Group TheGroup() { return new Group("OEM"); }
    public static Group OtherGroup() { return new Group("CMB"); }

}
Bryan Rowe
la source
2

Je voudrais simplement créer un dictionnaire et utiliser le code comme clé.

Edit: Pour répondre au commentaire sur la recherche inversée (trouver la clé), ce ne serait pas terriblement efficace. Si cela est nécessaire, j'écrirais une nouvelle classe pour le gérer.

jhale
la source
Pouvez-vous également saisir facilement une clé pour une valeur donnée?
eglasius
À C.Ross - Je ne sais pas ce que tu veux dire. Vous pouvez lire les valeurs dans une base de données et remplir dynamiquement le dictionnaire.
jhale
2

Ma première question - Avez-vous accès à la base de données elle-même? Cela devrait être normalisé dans la base de données, idéalement, sinon, toute solution sera sujette à erreur. D'après mon expérience, les champs de données remplis de "OEM" et "CMB" ont tendance à se retrouver avec des choses comme "OEM" et d'autres "données de merde" mélangées au fil du temps .... Si vous pouvez les normaliser, vous pouvez utiliser la clé dans le tableau contenant les éléments comme votre Enum, et vous avez terminé, avec une structure beaucoup plus propre.

Si ce n'est pas disponible, je ferais votre Enum et ferais une classe pour analyser votre chaîne dans l'Enum pour vous. Cela vous donnerait au moins une certaine souplesse dans la gestion des entrées non standard et beaucoup plus de flexibilité pour le piégeage ou la gestion des erreurs que pour toute solution de contournement utilisant Enum.Parse / Reflection / etc. Un dictionnaire fonctionnerait, mais pourrait tomber en panne si vous rencontrez des problèmes de cas, etc.

Je recommanderais d'écrire un cours pour que vous puissiez faire:

// I renamed this to GroupType, since it sounds like each element has a single type...
GroupType theType = GroupTypeParser.GetGroupType(theDBString);

Cela préserve la plupart de votre lisibilité sans avoir à modifier la base de données.

Reed Copsey
la source
2

Si je comprends bien, vous avez besoin d'une conversion de chaîne en énumération:

enum GroupTypes {
    Unknown = 0,
    OEM = 1,
    CMB = 2
}
static GroupTypes StrToEnum(string str){
    GroupTypes g = GroupTypes.Unknown;
    try {
        object o = Enum.Parse(typeof(GroupTypes), str, true);
        g = (GroupTypes)(o ?? 0);
    } catch {
    }
    return g;
}
// then use it like this
GroupTypes g1 = StrToEnum("OEM");
GroupTypes g2 = StrToEnum("bad value");

Vous pouvez le rendre plus sophistiqué avec des génériques pour le type d'énumération si vous le souhaitez.

dmihailescu
la source
2

Dans VS 2015, vous pouvez utiliser nameof

public class LogCategory
{
    public static string Trace;
    public static string Debug;
    public static string Info;
    public static string Warning;
    public static string Error;
}

Usage:

Logger.Write("This is almost like an enum.", nameof(LogCategory.Info));
Claus Elmann
la source
2

Un petit ajustement à la méthode Glennular Extension, vous pouvez donc utiliser l'extension sur autre chose que sur ENUM;

using System;
using System.ComponentModel;
namespace Extensions {
    public static class T_Extensions {
        /// <summary>
        /// Gets the Description Attribute Value
        /// </summary>
        /// <typeparam name="T">Entity Type</typeparam>
        /// <param name="val">Variable</param>
        /// <returns>The value of the Description Attribute or an Empty String</returns>
        public static string Description<T>(this T t) {
            DescriptionAttribute[] attributes = (DescriptionAttribute[])t.GetType().GetField(t.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
            return attributes.Length > 0 ? attributes[0].Description : string.Empty;
        }
    }
}

Ou en utilisant Linq

using System;
using System.ComponentModel;
using System.Linq;

namespace Extensions {


public static class T_Extensions {
        public static string Description<T>(this T t) =>
            ((DescriptionAttribute[])t
            ?.GetType()
            ?.GetField(t?.ToString())
            ?.GetCustomAttributes(typeof(DescriptionAttribute), false))
            ?.Select(a => a?.Description)
            ?.FirstOrDefault() 
            ?? string.Empty;  
    }
}
Mike
la source
2

Suite à la réponse de @Even Mien j'ai essayé d'aller un peu plus loin et de le rendre générique, il me semble que j'y suis presque mais un cas résiste toujours et je peux probablement simplifier un peu mon code.
Je le poste ici si quelqu'un voit comment je pourrais m'améliorer et surtout le faire fonctionner car je ne peux pas l'affecter à partir d'une chaîne

Jusqu'à présent, j'ai les résultats suivants:

        Console.WriteLine(TestEnum.Test1);//displays "TEST1"

        bool test = "TEST1" == TestEnum.Test1; //true

        var test2 = TestEnum.Test1; //is TestEnum and has value

        string test3 = TestEnum.Test1; //test3 = "TEST1"

        var test4 = TestEnum.Test1 == TestEnum.Test2; //false
         EnumType<TestEnum> test5 = "TEST1"; //works fine

        //TestEnum test5 = "string"; DOESN'T compile .... :(:(

Où la magie opère:

public abstract  class EnumType<T>  where T : EnumType<T>   
{

    public  string Value { get; set; }

    protected EnumType(string value)
    {
        Value = value;
    }


    public static implicit operator EnumType<T>(string s)
    {
        if (All.Any(dt => dt.Value == s))
        {
            Type t = typeof(T);

            ConstructorInfo ci = t.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic,null, new Type[] { typeof(string) }, null);

            return (T)ci.Invoke(new object[] {s});
        }
        else
        {
            return null;
        }
    }

    public static implicit operator string(EnumType<T> dt)
    {
        return dt?.Value;
    }


    public static bool operator ==(EnumType<T> ct1, EnumType<T> ct2)
    {
        return (string)ct1 == (string)ct2;
    }

    public static bool operator !=(EnumType<T> ct1, EnumType<T> ct2)
    {
        return !(ct1 == ct2);
    }


    public override bool Equals(object obj)
    {
        try
        {
            return (string)obj == Value;
        }
        catch
        {
            return false;
        }
    }

    public override int GetHashCode()
    {
        return Value.GetHashCode();
    }

    public static IEnumerable<T> All
     => typeof(T).GetProperties()
       .Where(p => p.PropertyType == typeof(T))
       .Select(x => (T)x.GetValue(null, null));



}

Je n'ai qu'à déclarer cela pour mes énumérations:

public class TestEnum : EnumType<TestEnum> 
{

    private TestEnum(string value) : base(value)
    {}

    public static TestEnum Test1 { get { return new TestEnum("TEST1"); } }
    public static TestEnum Test2 { get { return new TestEnum("TEST2"); } }
}
Lomithrani
la source
Merci pour ce beau travail, je cherchais une telle approche depuis longtemps. Je pense que vous devriez obtenir 1000 points pour cela
user3492977
Oh merci pour ce commentaire, et merci de me le rappeler, je n'avais pas utilisé c # depuis deux ans quand j'ai écrit ce bout de code, je devrais y revenir bientôt!
Lomithrani
@ user3492977 J'y suis finalement revenu et l'ai rendu entièrement fonctionnel, je doute toujours que ce soit une bonne idée ou une chose inutile: D stackoverflow.com/questions/62043138/…
Lomithrani
2

Nouveau dans .Net Core 3.0 / C # 8.0 (si votre environnement de travail vous permet de mettre à niveau votre projet) est une instruction de commutateur abrégée qui semble quelque peu énumérée. À la fin de la journée, c'est la même vieille déclaration de commutateur ennuyeux que nous utilisons depuis des années.

La seule vraie différence ici est que l'instruction switch a obtenu un nouveau costume.

public static RGBColor FromRainbow(Rainbow colorBand) =>
colorBand switch
{
    Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
    Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
    Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
    Rainbow.Green  => new RGBColor(0x00, 0xFF, 0x00),
    Rainbow.Blue   => new RGBColor(0x00, 0x00, 0xFF),
    Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
    Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
    _              => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
};

Vous remarquerez que le code ci-dessus que j'ai copié à partir d' ici utilise en fait une énumération comme paramètre.

Ce n'est pas exactement ce que vous voulez (et croyez-moi, je voulais quelque chose de similaire à ce que l'OP demande depuis longtemps), mais j'ai en fait l'impression que c'est un peu une branche d'olivier de MS. JMO.

J'espère que cela aide quelqu'un!

user3180664
la source
2

J'ai utilisé une structure telle que mentionnée dans une réponse précédente, mais j'ai supprimé toute complexité. Pour moi, cela ressemblait plus à la création d'une énumération de chaînes. Il est utilisé de la même manière qu'une énumération est utilisée.

    struct ViewTypes
    {
        public const string View1 = "Whatever string you like";
        public const string View2 = "another string";
    }

Exemple d'utilisation:

   switch( some_string_variable )
   {
      case ViewTypes.View1: /* do something */ break;
      case ViewTypes.View2: /* do something else */ break;
   }
Scott
la source
1

J'ai même implémenté quelques énumérations comme suggéré par @Even (via class Xet public static Xmembres), juste pour découvrir plus tard que ces jours-ci, à partir de .Net 4.5, il y a le droit ToString() méthode.

Maintenant, je réimplémente tout en énumérant.

bohdan_trotsenko
la source
1

C'est une façon de l'utiliser comme paramètre fortement typé ou comme chaîne :

public class ClassLikeEnum
{
    public string Value
    {
        get;
        private set;
    }

    ClassLikeEnum(string value) 
    {
        Value = value;
    }

    public static implicit operator string(ClassLikeEnum c)
    {
        return c.Value;
    }

    public static readonly ClassLikeEnum C1 = new ClassLikeEnum("RandomString1");
    public static readonly ClassLikeEnum C2 = new ClassLikeEnum("RandomString2");
}
Nicolas Bodin-Ripert
la source
1

Vous pouvez utiliser deux énumérations. Un pour la base de données et l'autre pour la lisibilité.

Vous avez juste besoin de vous assurer qu'ils restent synchronisés, ce qui semble être un petit coût. Il n'est pas nécessaire de définir les valeurs, il suffit de définir les positions de la même manière, mais la définition des valeurs indique clairement que les deux énumérations sont liées et empêche les erreurs de réorganiser les membres de l'énumération. Et un commentaire permet à l'équipe de maintenance de savoir qu'ils sont liés et doivent être synchronisés.

// keep in sync with GroupTypes
public enum GroupTypeCodes
{
    OEM,
    CMB
}

// keep in sync with GroupTypesCodes
public enum GroupTypes
{
    TheGroup = GroupTypeCodes.OEM,
    TheOtherGroup = GroupTypeCodes.CMB
}

Pour l'utiliser, il vous suffit de convertir d'abord le code:

GroupTypes myGroupType = GroupTypes.TheGroup;
string valueToSaveIntoDatabase = ((GroupTypeCodes)myGroupType).ToString();

Ensuite, si vous voulez le rendre encore plus pratique, vous pouvez ajouter une fonction d'extension qui ne fonctionne que pour ce type d'énumération:

public static string ToString(this GroupTypes source)
{
    return ((GroupTypeCodes)source).ToString();
}

et vous pouvez alors simplement faire:

GroupTypes myGroupType = GroupTypes.TheGroup;
string valueToSaveIntoDatabase = myGroupType.ToString();
toddmo
la source
C'est une mauvaise pratique: avec une personne à charge, enumun changement de valeur prévu dans l'un peut involontairement gâcher l'autre.
Lorenz Lo Sauer du
1

Je cherchais essentiellement la réponse de réflexion par @ArthurC

Juste pour étendre un peu sa réponse, vous pouvez la rendre encore meilleure en ayant une fonction générique:

    // If you want for a specific Enum
    private static string EnumStringValue(GroupTypes e)
    {
        return EnumStringValue<GroupTypes>(e);
    }

    // Generic
    private static string EnumStringValue<T>(T enumInstance)
    {
        return Enum.GetName(typeof(T), enumInstance);
    } 

Ensuite, vous pouvez simplement envelopper tout ce que vous avez

EnumStringValue(GroupTypes.TheGroup) // if you incorporate the top part

ou

EnumStringValue<GroupTypes>(GroupTypes.TheGroup) // if you just use the generic
ThinkBonobo
la source
1

Tiré de @EvenMien et ajouté dans certains des commentaires. (Aussi pour mon propre cas d'utilisation)

public struct AgentAction
{
    private AgentAction(string value) { Value = value; }

    public string Value { get; private set; }

    public override string ToString()
    {
        return this.Value;
    }

    public static AgentAction Login = new AgentAction("Logout");
    public static AgentAction Logout = new AgentAction("Logout");

    public static implicit operator string(AgentAction action) { return action.ToString(); }
}
moisissures
la source
1

Ajout de cette classe

public class DatabasePreference {
    public DatabasePreference([CallerMemberName] string preferenceName = "") {
        PreferenceName = preferenceName;
    }
    public string PreferenceName;
}

Ce travail utilise CallerMemberName pour minimiser le codage

En utilisant:

//Declare names
public static DatabasePreference ScannerDefaultFlashLight = new DatabasePreference();
public static DatabasePreference ScannerQrCodes = new DatabasePreference();
public static DatabasePreference Scanner1dCodes = new DatabasePreference();

Essaye-le:

Console.WriteLine(ScannerDefaultFlashLight.PreferenceName);
Console.WriteLine(ScannerDefaultFlashLight.Scanner1dCodes);

production:

ScannerDefaultFlashLight
Scanner1dCodes
nyconing
la source
0

Basé sur d'autres opinions, c'est ce que je propose. Cette approche évite d'avoir à taper .Value où vous voulez obtenir la valeur constante.

J'ai une classe de base pour toutes les énumérations de chaînes comme celle-ci:

using System;
using Newtonsoft.Json;

[JsonConverter(typeof(ConstantConverter))]
public class StringEnum: IConvertible
{
    public string Value { get; set; }

    protected StringEnum(string value)
    {
        Value = value;
    }

    public static implicit operator string(StringEnum c)
    {
        return c.Value;
    }
    public string ToString(IFormatProvider provider)
    {
        return Value;
    }

    public TypeCode GetTypeCode()
    {
        throw new NotImplementedException();
    }

    public bool ToBoolean(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }
    //The same for all the rest of IConvertible methods
}

Le JsonConverter est comme ceci:

using System;
using Newtonsoft.Json;

class ConstantConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value == null)
        {
            serializer.Serialize(writer, null);
        }
        else
        {
            serializer.Serialize(writer, value.ToString());
        }
    }
}

Et une énumération de chaîne réelle ressemblera à ceci:

public sealed class Colors : StringEnum
{
    public static Colors Red { get { return new Catalog("Red"); } }
    public static Colors Yellow { get { return new Catalog("Yellow"); } }
    public static Colors White { get { return new Catalog("White"); } }

    private Colors(string value) : base(value) { }
}

Et avec cela, vous pouvez simplement utiliser Color.Red pour sérialiser même en json sans utiliser la propriété Value

ayepiz
la source
0

Je n'avais besoin de rien de solide comme stocker la chaîne dans des attributs. Je devais juste transformer quelque chose comme MyEnum.BillEveryWeek"facture chaque semaine" ou MyEnum.UseLegacySystem"utiliser le système hérité" - essentiellement diviser l'énumération par son boîtier de chameau en mots individuels en minuscules.

public static string UnCamelCase(this Enum input, string delimiter = " ", bool preserveCasing = false)
{
    var characters = input.ToString().Select((x, i) =>
    {

       if (i > 0 && char.IsUpper(x))
       {
           return delimiter + x.ToString(CultureInfo.InvariantCulture);
       }
       return x.ToString(CultureInfo.InvariantCulture);

    });

    var result = preserveCasing
       ? string.Concat(characters)
       : string.Concat(characters).ToLower();

    var lastComma = result.LastIndexOf(", ", StringComparison.Ordinal);

    if (lastComma > -1)
    {
       result = result.Remove(lastComma, 2).Insert(lastComma, " and ");
    }

    return result;
}

MyEnum.UseLegacySystem.UnCamelCase() sorties "utiliser le système hérité"

Si plusieurs drapeaux sont définis, cela le transformera en anglais simple (délimité par des virgules sauf un "et" à la place de la dernière virgule).

var myCustomerBehaviour = MyEnum.BillEveryWeek | MyEnum.UseLegacySystem | MyEnum.ChargeTaxes;

Console.WriteLine(myCustomerBehaviour.UnCamelCase());
//outputs "bill every week, use legacy system and charge taxes"
Chad Hedgcock
la source