web-dev-qa-db-fra.com

Association d'énums avec des chaînes en C #

Je sais que ce qui suit n'est pas possible car il doit s'agir d'un

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

Dans ma base de données, je reçois un champ avec des codes incompréhensibles (OEM et CMB). Je voudrais faire de ce domaine une énumération ou autre chose compréhensible. Comme la cible est la lisibilité, la solution doit être concise.
Quelles autres options ai-je? 

236
Boris Callens

J'aime utiliser les propriétés dans une classe au lieu de méthodes, car elles ressemblent davantage à 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 les valeurs de chaîne de caractères de type 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);
261
Even Mien

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());
121
Glennular

Pourquoi ne pas utiliser une classe statique avec des constantes? Le code client ne sera pas différent des enums.

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

void DoSomething(GroupTypes groupType)
{
  if(groupType == GroupTypes.TheOtherGroup)
  {
    //Launch nuclear bomb 
  }  
}
68
rpattabi

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 sur les champs statiques du type de l’énumération (dans ce cas, GroupTypes) et obtenez le DescriptionAttribute pour la 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 renvoyer la variable DescriptionAttribute ci-dessus, au cas où vous souhaiteriez pouvoir déterminer si l'attribut est même appliqué ou non.

27
casperOne

Vous pouvez le faire très facilement en réalité. 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 par le passé et j'ai également utilisé une classe de constantes pour contenir des constantes de chaîne. Les deux méthodes fonctionnent assez bien, mais j'ai tendance à préférer cela.

17
Arthur C

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 convertir ceux-ci en int et récupérer les valeurs correctes de l'énumération correcte que vous souhaitez utiliser ultérieurement dans votre modèle.

13
Dave Van den Eynde

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.");
    }
}
9
C. Ross

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

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

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

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());  
    }
}
6
azamsharp

Une autre façon de traiter le problème consiste à avoir une énumération et un tableau de chaînes qui mapperont les valeurs enum avec la liste de chaînes:

public enum GroupTypes
{
    TheGroup  = 0,
    TheOtherGroup 
}

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

vous pouvez l'utiliser comme ceci:

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

Il sera invite CMB

AVANTAGES:

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

LES INCONVÉNIENTS:

  1. Sujet à gâcher la liste lors de son édition, mais cela ira pour une courte liste
6
Luis Orantes

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.

3
Jim Mischel

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

Par exemple. Pour imprimer les résultats réussite/échec:

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

Répondre par Even: 

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

Je voulais juste ajouter un moyen d'imiter un commutateur avec des énumérations basées sur la classe: 

public void Foo(LogCategory logCategory){    

  var @switch = new Dictionary<LogCategory, Action>{
    {LogCategory.Trace, ()=>Console.Writeline("Trace selected!")},
    {LogCategory.Debug, ()=>Console.Writeline("Debug selected!")},
    {LogCategory.Error, ()=>Console.Writeline("Error selected!")}};

   //will print one of the line based on passed argument
  @switch[logCategory]();
}
3
Arnis Lapsa
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; }
    }
}
2
Roman M

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

Éditer: Pour adresser le commentaire sur la recherche inversée (trouver la clé), cela ne serait pas très efficace. Si cela est nécessaire, j'écrirais une nouvelle classe pour le gérer.

2
jhale

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

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 enum si vous le souhaitez.

2
dmihailescu

Si vous essayez de rendre votre code lisible:

class GroupTypes {
    public static final String (whatever oem stands for) = "OEM";
    public static final String (whatever cmb stands for) = "CMB";
    ...
}

et si vous en avez besoin d'une liste, incluez ces finales dans une liste finale statique <String>. Cet exemple est en Java.

Si vous essayez de rendre votre application lisible, ajoutez:

public static final Map<String, String> groupsByDbValue;
static {
    groupsByDbValue = new HashMap<String, String>();
    groupsByDbValue.put("OEM", "(whatever OEM stands for)");
    groupsByDbValue.put("CMB", "(whatever CMB stands for)");
}
2
Kevin Conner

J'en ferais une classe et éviterais une énumération. Et ensuite, avec l'utilisation d'un gestionnaire de types, vous pouvez créer l'objet lorsque vous le récupérez à partir de 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"); }

}
2
Bryan Rowe

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));
2
Claus Elmann

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 risque de générer des erreurs. D'après mon expérience, les champs de données remplis de "OEM" et de "CMB" ont tendance à finir par contenir des éléments tels que "OEM" et d'autres "données de merde" mélangées au fil du temps ... Si vous pouvez les normaliser, utilisez la clé dans le tableau contenant les éléments que votre Enum, et vous avez terminé, avec une structure beaucoup plus propre.

Si ce n'est pas disponible, je ferais votre Enum, et je ferais un cours pour analyser votre chaîne dans le Enum pour vous. Cela vous donnerait au moins une certaine flexibilité dans la gestion des entrées non standard et beaucoup plus grande pour le recouvrement ou la gestion des erreurs que la résolution de problèmes avec Enum.Parse/Reflection/etc. Un dictionnaire fonctionnerait, mais pourrait tomber en panne si vous avez 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 majeure partie de votre lisibilité sans avoir à changer de base de données.

2
Reed Copsey

Une petite méthode d'extension Tweak to Glennular, vous pouvez donc utiliser l'extension sur d'autres choses que celle d'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;  
    }
}
2
Mike

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

Vous devez simplement 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, mais de définir les positions de la même manière, mais le fait de définir les valeurs indique clairement que les deux énumérations sont liées et empêche les erreurs de réorganiser les membres enum. Et un commentaire indique à l'équipe de maintenance que ces problèmes 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, vous devez d'abord convertir 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();
1
toddmo

les énumérations en C # sont limitées aux types numériques sous-jacents (octets, octets, short, ushort, int, uint, long et ulong). Vous ne pouvez pas les associer à une valeur sous-jacente basée sur un caractère ou une chaîne.

Une approche différente pourrait être de définir un dictionnaire de type Dictionnaire <chaîne, chaîne>.

1
andleer

C'est une façon de l'utiliser en tant que paramètre fortement typé ou en tant que 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");
}
1

J'ai même implémenté quelques enums suggérés par @Even (via les membres class X et public static X), juste pour savoir plus tard que, à partir de .Net 4.5, il y a the rightToString() method.

Maintenant, je réimplémente tout en enums.

1
bohdan_trotsenko

Je cherchais essentiellement la réponse Reflection de @ArthurC

Juste pour étendre un peu sa réponse, vous pouvez l’améliorer encore avec 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
1
ThinkBonobo

Je voulais éviter d'utiliser complètement les littéraux de chaîne et je n'avais pas besoin d'espace dans la description des éléments. Plus important encore, je voulais avoir un mécanisme pour vérifier si la chaîne fournie est un élément valide, alors j'ai proposé cette solution:

public class Seasons
{
    public static string Spring { get; }
    public static string Summer { get; }
    public static string Fall { get; }
    public static string Winter { get; }

    public static bool IsValid(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName))
        {
            return false;
        }

        try
        {           
            return typeof(Seasons).GetProperty(propertyName) != null;
        }
        catch
        {
            return false;
        }       
    }
}

Et voici comment cela fonctionne:

void Main()
{
    string s = nameof(Seasons.Fall);
    Console.WriteLine($"Fall is valid: {Seasons.IsValid(s)}"); // true

    s = "WrongSeason";
    Console.WriteLine($"WrongSeason is valid: {Seasons.IsValid(s)}"); // false
}

J'ai essayé de refactoriser IsValid () dans une classe de base et d'utiliser réflexion pour lire le type (MethodBase.GetCurrentMethod (). DeclaringType), mais comme je voulais l'avoir statique, il retourne le type de classe de base, pas le type hérité. Votre remède à cela sera très bien accueilli! Voici ce que j'essayais de réaliser:

public  class Seasons : ConstantStringsBase
{
    // ... same
}

public  class ConstantStringsBase
{
    public static bool IsValid(string propertyName)
    {       
        return MethodBase.GetCurrentMethod().DeclaringType.GetProperty(propertyName) != null;
    }
}
0

Suite à la réponse de @Even Mien, j’ai essayé d’aller un peu plus loin et de le rendre générique. Je semble y être presque, mais un cas résiste encore 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'ici 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ù se passe la magie:

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 dois seulement alors déclarer ceci pour mes enums:

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"); } }
}
0
Lomithrani

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 tous les énums de chaînes comme ceci:

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 ça:

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 même sérialiser en json sans utiliser la propriété Value

0
ayepiz

Tiré de @EvenMien et ajouté à certains commentaires. (Également 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(); }
}
0
moldypenguins

J'ai fait quelque chose comme ça;

public enum BusinessUnits
{
    NEW_EQUIPMENT = 0,
    USED_EQUIPMENT = 1,
    RENTAL_EQUIPMENT = 2,
    PARTS = 3,
    SERVICE = 4,
    OPERATOR_TRAINING = 5
}

public class BusinessUnitService
{
    public static string StringBusinessUnits(BusinessUnits BU)
    {
        switch (BU)
        {
            case BusinessUnits.NEW_EQUIPMENT: return "NEW EQUIPMENT";
            case BusinessUnits.USED_EQUIPMENT: return "USED EQUIPMENT";
            case BusinessUnits.RENTAL_EQUIPMENT: return "RENTAL EQUIPMENT";
            case BusinessUnits.PARTS: return "PARTS";
            case BusinessUnits.SERVICE: return "SERVICE";
            case BusinessUnits.OPERATOR_TRAINING: return "OPERATOR TRAINING";
            default: return String.Empty;
        }
    }
}

Appelez ça avec ça;

BusinessUnitService.StringBusinessUnits(BusinessUnits.PARTS)
0
Mahib

Je n'avais besoin de rien de plus robuste que de stocker la chaîne dans des attributs. J'avais juste besoin de transformer quelque chose comme MyEnum.BillEveryWeek en "facture toutes les semaines" ou MyEnum.UseLegacySystem en "utiliser le système hérité" - en gros, divisez l'énum en le recouvrant de son chameau en mots 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() génère "use legacy system"

Si vous avez plusieurs drapeaux définis, cela convertira cela 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"
0
Chad Hedgcock