Как определить перечисление со строковым значением? - PullRequest
76 голосов
/ 21 декабря 2011

Я пытаюсь определить Enum и добавить допустимые общие разделители, которые используются в CSV или аналогичных файлах. Затем я собираюсь связать его с ComboBox в качестве источника данных, поэтому при каждом добавлении или удалении из определения Enum мне не нужно ничего менять в поле со списком.

Проблема в том, как определить enum с помощью строкового представления, например:

public enum SeparatorChars{Comma = ",", Tab = "\t", Space = " "}

Ответы [ 14 ]

98 голосов
/ 21 декабря 2011

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

enum Separator
{
    Comma = ',',
    Tab = '\t',
    Space = ' '
}

(РЕДАКТИРОВАТЬ: просто чтобы уточнить, вы не можете сделать char базовый тип перечисления, но вы можете использовать char константы, чтобы назначить интегральное значение, соответствующее каждому значению перечисления. Базовый тип выше перечисление int.)

Тогда метод расширения, если он вам нужен:

public string ToSeparatorString(this Separator separator)
{
    // TODO: validation
    return ((char) separator).ToString();
}
69 голосов
/ 21 декабря 2011

Насколько я знаю, вам не разрешат присваивать строковые значения для enum. Что вы можете сделать, это создать класс со строковыми константами в нем.

public static class SeparatorChars
{
    public static String Comma { get { return ",";} } 
    public static String Tab { get { return "\t,";} } 
    public static String Space { get { return " ";} } 
}
53 голосов
/ 21 декабря 2011

Вы можете достичь этого, но потребуется немного работы.

  1. Определить класс атрибута, который будет содержать строковое значение для enum.
  2. Определить метод расширения, который будет возвращать значение из атрибута. Например, GetStringValue (это значение Enum) вернет значение атрибута.
  3. Тогда вы можете определить перечисление следующим образом.
public enum Test : int {
    [StringValue("a")]
    Foo = 1,
    [StringValue("b")]
    Something = 2        
} 
  1. Чтобы вернуть значение из Attrinbute Test.Foo.GetStringValue ();

См .: Перечисление со строковыми значениями в C #

25 голосов
/ 21 декабря 2011

Вы не можете сделать это с помощью перечислений, но вы можете сделать это так:

public static class SeparatorChars
{
    public static string Comma = ",";

    public static string Tab = "\t";

    public static string Space = " ";
}
22 голосов
/ 23 мая 2016

Для простого перечисления строковых значений (или любого другого типа):

public static class MyEnumClass
{
    public const string 
        MyValue1 = "My value 1",
        MyValue2 = "My value 2";
}

Использование: string MyValue = MyEnumClass.MyValue1;

12 голосов
/ 21 декабря 2011

Вы не можете, потому что enum может быть основан только на примитивном числовом типе. Вы можете попробовать использовать Dictionary вместо:

Dictionary<String, char> separators = new Dictionary<string, char>
{
    {"Comma", ','}, 
    {"Tab",  '\t'}, 
    {"Space", ' '},
};

В качестве альтернативы вы можете использовать Dictionary<Separator, char> или Dictionary<Separator, string>, где Separator - это обычное перечисление:

enum Separator
{
    Comma,
    Tab,
    Space
}

, что было бы немного приятнее, чем прямая обработка строк.

6 голосов
/ 06 февраля 2018

Класс, имитирующий поведение перечисления, но использующий string вместо int, может быть создан следующим образом ...

public class GrainType
{
    private string _typeKeyWord;

    private GrainType(string typeKeyWord)
    {
        _typeKeyWord = typeKeyWord;
    }

    public override string ToString()
    {
        return _typeKeyWord;
    }

    public static GrainType Wheat = new GrainType("GT_WHEAT");
    public static GrainType Corn = new GrainType("GT_CORN");
    public static GrainType Rice = new GrainType("GT_RICE");
    public static GrainType Barley = new GrainType("GT_BARLEY");

}

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

GrainType myGrain = GrainType.Wheat;

PrintGrainKeyword(myGrain);

тогда ...

public void PrintGrainKeyword(GrainType grain) 
{
    Console.Writeline("My Grain code is " + grain.ToString());   // Displays "My Grain code is GT_WHEAT"
}
5 голосов
/ 02 июля 2018

Уже довольно поздно для ответа, но, возможно, это поможет кому-то в будущем.Мне было проще использовать struct для такого рода проблем.

Следующий пример является копией вставленной части из кода MS:

namespace System.IdentityModel.Tokens.Jwt
{
    //
    // Summary:
    //     List of registered claims from different sources http://tools.ietf.org/html/rfc7519#section-4
    //     http://openid.net/specs/openid-connect-core-1_0.html#IDToken
    public struct JwtRegisteredClaimNames
    {
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Actort = "actort";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Typ = "typ";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Sub = "sub";
        //
        // Summary:
        //     http://openid.net/specs/openid-connect-frontchannel-1_0.html#OPLogout
        public const string Sid = "sid";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Prn = "prn";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Nbf = "nbf";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Nonce = "nonce";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string NameId = "nameid";

    }
}
3 голосов
/ 20 декабря 2018

Для тех, кто приезжает сюда и ищет ответ на более общий вопрос, вы можете расширить концепцию статического класса, если хотите, чтобы ваш код выглядел как enum.

Следующий подход работает, когда вы не завершили желаемое enum names, а enum values - это string представление enam name; используйте nameof(), чтобы упростить рефакторинг.

public static class Colours
{
    public static string Red => nameof(Red);
    public static string Green => nameof(Green);
    public static string Blue => nameof(Blue);
}

Достигается намерение перечисления со строковыми значениями (например, следующий псевдокод):

public enum Colours
{
    "Red",
    "Green",
    "Blue"
}
2 голосов
/ 30 октября 2018

Опираясь на некоторые ответы здесь, я реализовал многократно используемый базовый класс, который имитирует поведение перечисления, но с string в качестве базового типа.Он поддерживает различные операции, включая:

  1. получение списка возможных значений
  2. с преобразованием в строку
  3. сравнение с другими экземплярами с помощью .Equals, == и!=
  4. преобразование в / из JSON с использованием JSON.NET JsonConverter

Это базовый класс во всей своей полноте:

public abstract class StringEnumBase<T> : IEquatable<T>
    where T : StringEnumBase<T>
{
    public string Value { get; }

    protected StringEnumBase(string value) => this.Value = value;

    public override string ToString() => this.Value;

    public static List<T> AsList()
    {
        return typeof(T)
            .GetProperties(BindingFlags.Public | BindingFlags.Static)
            .Where(p => p.PropertyType == typeof(T))
            .Select(p => (T)p.GetValue(null))
            .ToList();
    }

    public static T Parse(string value)
    {
        List<T> all = AsList();

        if (!all.Any(a => a.Value == value))
            throw new InvalidOperationException($"\"{value}\" is not a valid value for the type {typeof(T).Name}");

        return all.Single(a => a.Value == value);
    }

    public bool Equals(T other)
    {
        if (other == null) return false;
        return this.Value == other?.Value;
    }

    public override bool Equals(object obj)
    {
        if (obj == null) return false;
        if (obj is T other) return this.Equals(other);
        return false;
    }

    public override int GetHashCode() => this.Value.GetHashCode();

    public static bool operator ==(StringEnumBase<T> a, StringEnumBase<T> b) => a?.Equals(b) ?? false;

    public static bool operator !=(StringEnumBase<T> a, StringEnumBase<T> b) => !(a?.Equals(b) ?? false);

    public class JsonConverter<T> : Newtonsoft.Json.JsonConverter
        where T : StringEnumBase<T>
    {
        public override bool CanRead => true;

        public override bool CanWrite => true;

        public override bool CanConvert(Type objectType) => ImplementsGeneric(objectType, typeof(StringEnumBase<>));

        private static bool ImplementsGeneric(Type type, Type generic)
        {
            while (type != null)
            {
                if (type.IsGenericType && type.GetGenericTypeDefinition() == generic)
                    return true;

                type = type.BaseType;
            }

            return false;
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JToken item = JToken.Load(reader);
            string value = item.Value<string>();
            return StringEnumBase<T>.Parse(value);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            if (value is StringEnumBase<T> v)
                JToken.FromObject(v.Value).WriteTo(writer);
        }
    }
}

И этокак бы вы реализовали свое «перечисление строк»:

[JsonConverter(typeof(JsonConverter<Colour>))]
public class Colour : StringEnumBase<Colour>
{
    private Colour(string value) : base(value) { }

    public static Colour Red => new Colour("red");
    public static Colour Green => new Colour("green");
    public static Colour Blue => new Colour("blue");
}

Что можно использовать следующим образом:

public class Foo
{
    public Colour colour { get; }

    public Foo(Colour colour) => this.colour = colour;

    public bool Bar()
    {
        if (this.colour == Colour.Red || this.colour == Colour.Blue)
            return true;
        else
            return false;
    }
}

Надеюсь, кто-то найдет это полезным!

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