Связывание перечислений со строками в C # - PullRequest
264 голосов
/ 10 марта 2009

Я знаю, что следующее невозможно, потому что оно должно быть int

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

Из моей базы данных я получаю поле с непонятными кодами (OEM и CMB). Я хотел бы превратить это поле в перечисление или что-то еще понятное. Поскольку целью является удобочитаемость, решение должно быть кратким.
Какие еще варианты у меня есть?

Ответы [ 28 ]

1 голос
/ 11 января 2017

В VS 2015 вы можете использовать nameof

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

Использование:

Logger.Write("This is almost like an enum.", nameof(LogCategory.Info));
1 голос
/ 06 августа 2016

Вы можете использовать два перечисления. Один для базы данных, а другой для удобства чтения.

Вам просто нужно убедиться, что они синхронизированы, что кажется небольшой ценой. Вам не нужно устанавливать значения, просто установите одинаковые позиции, но установка значений делает это очень ясным, поскольку два перечисления связаны, и предотвращает ошибки при перестановке членов перечисления. И комментарий позволяет команде обслуживания знать, что они связаны и должны быть синхронизированы.

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

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

Чтобы использовать его, сначала нужно преобразовать код:

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

Тогда, если вы хотите сделать это еще более удобным, вы можете добавить функцию расширения, которая работает только для перечислений этого типа:

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

и тогда вы можете просто сделать:

GroupTypes myGroupType = GroupTypes.TheGroup;
string valueToSaveIntoDatabase = myGroupType.ToString();
1 голос
/ 19 ноября 2015

Я даже реализовал несколько перечислений, как предложено @Even (через class X и public static X участников), просто чтобы позже узнать, что в эти дни, начиная с .Net 4.5, есть правильное ToString() метод.

Теперь я перевоплощаю все обратно в перечисления.

0 голосов
/ 06 июня 2019

Следуя ответу @Even Mien, я попытался пойти немного дальше и сделать его универсальным, я, кажется, почти на месте, но один случай все еще сопротивляется, и я, вероятно, могу немного упростить свой код. Я публикую это здесь, если кто-нибудь увидит, как я могу улучшить и особенно заставить его работать, поскольку я не могу назначить его из строки

Пока у меня есть следующие результаты:

        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 .... :(:(

Где происходит волшебство:

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



}

Я только тогда должен объявить это для моих перечислений:

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 голосов
/ 08 марта 2019

Я хотел избежать полного использования строковых литералов, а также мне не нужно было места в описании предметов. Что еще более важно, я хотел иметь механизм для проверки, является ли предоставленная строка допустимым элементом, поэтому я пришел к такому решению:

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

А вот как это работает:

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
}

Я попытался преобразовать IsValid () в базовый класс и использовать отражение для чтения типа (MethodBase.GetCurrentMethod (). DeclaringType), но, поскольку я хотел сделать его статическим, он возвращает тип базового класса, а не унаследованный тип. Ваше лекарство от этого будет очень приветствоваться! Вот что я пытался достичь:

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

public  class ConstantStringsBase
{
    public static bool IsValid(string propertyName)
    {       
        return MethodBase.GetCurrentMethod().DeclaringType.GetProperty(propertyName) != null;
    }
}
0 голосов
/ 07 июля 2018

Я сделал что-то вроде этого;

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

Позвони с этим;

BusinessUnitService.StringBusinessUnits(BusinessUnits.PARTS)
0 голосов
/ 08 февраля 2015

Исходя из других мнений, это то, что я придумал. Этот подход позволяет избежать ввода значения .Value там, где вы хотите получить постоянное значение.

У меня есть базовый класс для всех строковых перечислений, как это:

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
}

JsonConverter выглядит так:

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

И фактическое перечисление строк будет примерно таким:

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

И с этим вы можете просто использовать Color.Red даже для сериализации в json без использования свойства Value

0 голосов
/ 20 июля 2016

Мне не нужно ничего надежного, как хранение строки в атрибутах. Мне просто нужно было превратить что-то вроде MyEnum.BillEveryWeek в «счет каждую неделю» или MyEnum.UseLegacySystem в «использовать устаревшую систему» ​​- в основном разделить перечисление по верблюжьей оболочке на отдельные строчные слова.

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() выводит "использовать устаревшую систему"

Если у вас установлено несколько флагов, он превратит их в простой английский (с разделителями-запятыми, кроме «и» вместо последней запятой).

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

Console.WriteLine(myCustomerBehaviour.UnCamelCase());
//outputs "bill every week, use legacy system and charge taxes"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...