String değeri olan bir enum nasıl tanımlanır?


101

EnumCSV veya benzeri dosyalarda kullanılan geçerli bir ortak ayırıcılar tanımlamaya ve eklemeye çalışıyorum . Sonra onu ComboBoxbir veri kaynağı olarak bağlayacağım, böylece Enum tanımını her eklediğimde veya çıkardığımda, açılan kutuda hiçbir şeyi değiştirmem gerekmeyecek.

Sorun şu ki, dize gösterimi ile numaralandırmayı nasıl tanımlayabilirim, şöyle bir şey:

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


Yanıtlar:


116

Yapamazsınız - enum değerlerinin integral değerler olması gerekir. Her bir enum değeriyle bir dize değerini ilişkilendirmek için öznitelikleri kullanabilirsiniz veya bu durumda, her ayırıcı tek bir karakterse, yalnızca chardeğeri kullanabilirsiniz :

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

(DÜZENLEME: Sadece açıklığa kavuşturmak için, charnumaralandırmanın temelini oluşturamazsınız, ancak charher bir enum değerine karşılık gelen integral değeri atamak için sabitleri kullanabilirsiniz . Yukarıdaki numaralandırmanın temel türü int.)

Ardından, ihtiyacınız varsa bir uzatma yöntemi:

public string ToSeparatorString(this Separator separator)
{
    // TODO: validation
    return ((char) separator).ToString();
}

Karakter numaralandırmada geçerli değil. Msdn: "Her numaralandırma türünün temel bir türü vardır ve bu tür, char dışında herhangi bir integral türü olabilir."
dowhile için

9
@dowhilefor: Cevabıma göre, değer için bir karakter harfi kullanabilirsiniz . Test ettim :)
Jon Skeet

bu gereksinim dosyalar için olduğundan kullanıcı CRLF ayırıcısına ihtiyaç duyabilir. Bu durumda da işe yarayacak mı?
Maheep

Teşekkürler Jon, karakter olarak sayılmaz mı ?!
Saeid Yazdani

1
@ShaunLuttin: numaralandırmalar sadece "adlandırılmış sayılardır" - bu nedenle bir dize sıralaması bu modele gerçekten uymaz.
Jon Skeet

84

Bildiğim kadarıyla, numaralandırmaya dize değerleri atamanıza izin verilmeyecek. Yapabileceğiniz şey, içinde dize sabitleri olan bir sınıf oluşturmaktır.

public static class SeparatorChars
{
    public static String Comma { get { return ",";} } 
    public static String Tab { get { return "\t,";} } 
    public static String Space { get { return " ";} } 
}

10
Bu yaklaşımın diğerlerine zıt tarafı, ekstra / özel bir şey yapmadan bunları sıralayamayacağınızdır.
caesay

Bu, derleme sırasında belirli değerleri zorlamaya yardımcı olmaz, çünkü separatorartık Separatorkısıtlanmış geçerli değerlere sahip bir tür yerine bir dizedir (herhangi bir şey olabilir) .
ChickenFeet

74

Bunu başarabilirsin ama biraz çalışma gerektirecek.

  1. Enum için dize değerini içerecek bir nitelik sınıfı tanımlayın.
  2. Değeri öznitelikten geri döndürecek bir uzantı yöntemi tanımlayın. Örneğin..GetStringValue (bu Enum değeri) öznitelik değerini döndürecektir.
  3. Ardından numaralandırmayı bu şekilde tanımlayabilirsiniz ..
public enum Test: int {
    [StringValue ("a")]
    Foo = 1,
    [StringValue ("b")]
    Bir şey = 2        
} 
  1. Attrinbute Test.Foo.GetStringValue () öğesinden değeri geri almak için;

Bakınız: C # 'da Dize Değerleriyle Enum


6
Bunun eski olduğunu biliyorum, ancak açıkça benzersizdir ve DB'deki kod ve dize değerinde numaralandırmaları kullanmanıza izin verir. Amazing
A_kat

1
Geç bir yorum daha, ama bu gerçekten harika bir çözüm
Alan

38

Basit bir dize değerleri listesi için (veya başka herhangi bir tür):

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

Kullanım: string MyValue = MyEnumClass.MyValue1;


2
Bu bir sıralama olmasa da, bence bu, kullanıcının yapmaya çalıştığı şeye en iyi çözümü sağlayabilir. Bazen en basit çözüm en iyisidir.
Zesty

31

Bunu enums ile yapamazsınız, ancak şu şekilde yapabilirsiniz:

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

    public static string Tab = "\t";

    public static string Space = " ";
}

1
+1 Bunun doğru çözüm olduğunu düşünmeme rağmen, sınıfın adını değiştirirdim veya türünü karakter olarak değiştirirdim. Sadece tutarlı olmak için.
41'de

Teşekkürler, comboBox.DataSource = Enum.GetValues(typeof(myEnum));bu durumda neyin eşdeğeri olacağını söyleyebilir misiniz ?
Saeid Yazdani

1
@ Sean87: Buna sahip olmak istiyorsan, JonSkeets cevabını alırdım.
Fischermaen

Bence bu neredeyse doğru cevap, çünkü switch-caseblokların içinde kullanılamaz . Alanlar constsırayla olmalıdır. Ama istersen yine de yardımcı olamaz Enum.GetValues(typeof(myEnum)).
André Santaló

7
Onun constyerine kullanırdım static. Sabitler, statik olduğu kadar salt okunurdur ve yapıcılarda işaretlenemez (salt okunur alanlar olmadığı sürece).
Olivier Jacot-Descombes

13

Enum davranışını taklit eden ancak stringbunun yerine kullanan bir sınıf intaşağıdaki gibi oluşturulabilir ...

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

}

Kullanım ...

GrainType myGrain = GrainType.Wheat;

PrintGrainKeyword(myGrain);

sonra...

public void PrintGrainKeyword(GrainType grain) 
{
    Console.Writeline("My Grain code is " + grain.ToString());   // Displays "My Grain code is GT_WHEAT"
}

GrainType myGrain = "GT_CORN"Mesela tek şey yapamayacaksın .
colmde


12

Yapamazsınız, çünkü numaralandırma yalnızca ilkel bir sayısal türe dayalı olabilir. Bunun Dictionaryyerine a kullanmayı deneyebilirsiniz :

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

Alternatif olarak, a Dictionary<Separator, char>veya Dictionary<Separator, string>where Separatoris normal enum kullanabilirsiniz:

enum Separator
{
    Comma,
    Tab,
    Space
}

bu, dizeleri doğrudan kullanmaktan biraz daha hoş olurdu.


10

Cevap için biraz geç, ama belki gelecekte birine yardımcı olabilir. Bu tür bir problem için struct kullanmayı daha kolay buldum.

Aşağıdaki örnek, MS kodundan kopyalanıp yapıştırılan kısımdır:

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

    }
}

Bu yaklaşımın neden bir sınıf kullanmaktan daha iyi olduğunu açıklar mısınız?
Gerardo Grignoli

@GerardoGrignoli Bu tür şeyler için MS'te neden class yerine struct kullandıklarını tam olarak bilmiyorum. Bulmaya bile çalışmadım, çünkü bu benim için mükemmel çalışıyor. Belki burada
istifte

9

Belki çok geç, ama işte burada.

Enum değerlerini yönetmek için EnumMember özelliğini kullanabiliriz.

public enum EUnitOfMeasure
{
    [EnumMember(Value = "KM")]
    Kilometer,
    [EnumMember(Value = "MI")]
    Miles
}

Bu şekilde EUnitOfMeasure için sonuç değeri KM veya MI olacaktır. Bu aynı zamanda Andrew Whitaker'ın cevabında da görülebilir .


5

Buraya gelen ve daha genel bir soruya yanıt arayan kişiler için, kodunuzun bir enum.

Aşağıdaki yaklaşım, enum namesistediğinizi sonuçlandırmadığınızda işe yarar ve enum valuesaşağıdakilerin stringtemsilidir enam name; nameof()yeniden düzenlemenizi kolaylaştırmak için kullanın .

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

Bu, dize değerlerine (aşağıdaki sözde kod gibi) sahip bir enum amacına ulaşır:

public enum Colours
{
    "Red",
    "Green",
    "Blue"
}

4

.NET'te dize değerli numaralandırmalar oluşturmak için bir temel sınıf oluşturdum. Projelerinize kopyalayıp yapıştırabileceğiniz veya StringEnum adlı NuGet paketi aracılığıyla yükleyebileceğiniz tek bir C # dosyasıdır .

Kullanım:

///<completionlist cref="HexColor"/> 
class HexColor : StringEnum<HexColor>
{
    public static readonly HexColor Blue = New("#FF0000");
    public static readonly HexColor Green = New("#00FF00");
    public static readonly HexColor Red = New("#000FF");
}

Özellikleri

  • StringEnum değeriniz normal bir numaralandırmaya biraz benziyor:
    // Static Parse Method
    HexColor.Parse("#FF0000") // => HexColor.Red
    HexColor.Parse("#ff0000", caseSensitive: false) // => HexColor.Red
    HexColor.Parse("invalid") // => throws InvalidOperationException

    // Static TryParse method.
    HexColor.TryParse("#FF0000") // => HexColor.Red
    HexColor.TryParse("#ff0000", caseSensitive: false) // => HexColor.Red
    HexColor.TryParse("invalid") // => null

    // Parse and TryParse returns the preexistent instances
    object.ReferenceEquals(HexColor.Parse("#FF0000"), HexColor.Red) // => true

    // Conversion from your `StringEnum` to `string`
    string myString1 = HexColor.Red.ToString(); // => "#FF0000"
    string myString2 = HexColor.Red; // => "#FF0000" (implicit cast)
  • Intellisense, sınıfa xml yorumu eklenmişse, enum adını önerecektir <completitionlist>. (Hem C # hem de VB'de çalışır): yani

Intellisense demosu

Kurulum

Ya:

  • > = 1.0, > = 4.5, > = 4.6, vb. Üzerinde çalışacak şekilde en son StringEnum NuGet paketini yükleyin ..Net Standard 1.0.Net Core.Net FrameworkMono
  • Veya aşağıdaki StringEnum temel sınıfını projenize yapıştırın. ( son sürüm )
    public abstract class StringEnum<T> : IEquatable<T> where T : StringEnum<T>, new()
    {
        protected string Value;
        private static IList<T> valueList = new List<T>();
        protected static T New(string value)
        {
            if (value == null)
                return null; // the null-valued instance is null.

            var result = new T() { Value = value };
            valueList.Add(result);
            return result;
        }

        public static implicit operator string(StringEnum<T> enumValue) => enumValue.Value;
        public override string ToString() => Value;

        public static bool operator !=(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value != o2?.Value;
        public static bool operator ==(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value == o2?.Value;

        public override bool Equals(object other) => this.Value.Equals((other as T)?.Value ?? (other as string));
        bool IEquatable<T>.Equals(T other) => this.Value.Equals(other.Value);
        public override int GetHashCode() => Value.GetHashCode();

        /// <summary>
        /// Parse the <paramref name="value"/> specified and returns a valid <typeparamref name="T"/> or else throws InvalidOperationException.
        /// </summary>
        /// <param name="value">The string value representad by an instance of <typeparamref name="T"/>. Matches by string value, not by the member name.</param>
        /// <param name="caseSensitive">If true, the strings must match case sensitivity.</param>
        public static T Parse(string value, bool caseSensitive = false)
        {
            var result = TryParse(value, caseSensitive);
            if (result == null)
                throw new InvalidOperationException((value == null ? "null" : $"'{value}'") + $" is not a valid {typeof(T).Name}");

            return result;
        }

        /// <summary>
        /// Parse the <paramref name="value"/> specified and returns a valid <typeparamref name="T"/> or else returns null.
        /// </summary>
        /// <param name="value">The string value representad by an instance of <typeparamref name="T"/>. Matches by string value, not by the member name.</param>
        /// <param name="caseSensitive">If true, the strings must match case sensitivity.</param>
        public static T TryParse(string value, bool caseSensitive = false)
        {
            if (value == null) return null;
            if (valueList.Count == 0) System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); // force static fields initialization
            var field = valueList.FirstOrDefault(f => f.Value.Equals(value,
                    caseSensitive ? StringComparison.Ordinal
                                  : StringComparison.OrdinalIgnoreCase));
            // Not using InvariantCulture because it's only supported in NETStandard >= 2.0

            if (field == null)
                return null;

            return field;
        }
    }
  • İçin Newtonsoft.Jsonseri desteği, onun yerine bu genişletilmiş versiyonu kopyalayın. StringEnum.cs

Bu kodun Ben'in cevabına benzer olduğunu fark ettim. İçtenlikle sıfırdan yazdım. Bununla birlikte, <completitionlist>hack gibi birkaç ekstrası olduğunu düşünüyorum , ortaya çıkan sınıf daha çok bir Enum gibi görünüyor, Parse () üzerinde yansıma kullanmıyor, NuGet paketi ve gelen sorunları ve geri bildirimleri ele alacağımı umuyorum.


3

Buradaki yanıtlardan bazılarına dayanarak, bir numaralandırmanın davranışını taklit eden ancak stringtemel tür olan yeniden kullanılabilir bir temel sınıf uyguladım . Aşağıdakiler dahil çeşitli işlemleri destekler:

  1. olası değerlerin bir listesini almak
  2. dizgeye dönüştürme
  3. Diğer örneklerde ile karşılaştırılması ile .Equals, ==ve!=
  4. JSON.NET JsonConverter kullanarak JSON'a / JSON'dan dönüştürme

Bu, bütünüyle temel sınıftır:

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

Ve "dize numaranızı" şu şekilde uygularsınız:

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

Hangisi şu şekilde kullanılabilir:

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

Umarım birisi bunu yararlı bulur!


2

Öncelikle, tek bir karakter olsalar bile, karakterleri değil dizeleri atamayı deneyin. "," yerine "," kullanın. Sıradaki şey, numaralandırmalar, charsiz olmadan yalnızca integral türleri alır , ancak bunu yapmamanızı şiddetle tavsiye ederim. Bu değerlerin farklı kültürlerde ve dillerde aynı kalacağından eminseniz, sabit dizeleri olan statik bir sınıf kullanırdım.


2

Bir sıralama için temel olarak a charveya a kullanmak gerçekten mümkün olmasa da string, bence gerçekten yapmaktan hoşlandığınız şey bu değil.

Bahsettiğiniz gibi, bir olasılıklar listesine sahip olmak ve birleşik giriş kutusu içinde bunun bir dizi temsilini göstermek istiyorsunuz. Kullanıcı bu dize temsillerinden birini seçerse, karşılık gelen numaralandırmayı çıkarmak istersiniz. Ve bu mümkündür:

İlk önce bazı dizeleri bir enum değerine bağlamamız gerekir. Bu, burada veya buradaDescriptionAttribute anlatıldığı gibi yapılabilir .

Şimdi, enum değerlerinin ve karşılık gelen açıklamaların bir listesini oluşturmanız gerekir. Bu, aşağıdaki yöntem kullanılarak yapılabilir:

/// <summary>
/// Creates an List with all keys and values of a given Enum class
/// </summary>
/// <typeparam name="T">Must be derived from class Enum!</typeparam>
/// <returns>A list of KeyValuePair&lt;Enum, string&gt; with all available
/// names and values of the given Enum.</returns>
public static IList<KeyValuePair<T, string>> ToList<T>() where T : struct
{
    var type = typeof(T);

    if (!type.IsEnum)
    {
        throw new ArgumentException("T must be an enum");
    }

    return (IList<KeyValuePair<T, string>>)
            Enum.GetValues(type)
                .OfType<T>()
                .Select(e =>
                {
                    var asEnum = (Enum)Convert.ChangeType(e, typeof(Enum));
                    return new KeyValuePair<T, string>(e, asEnum.Description());
                })
                .ToArray();
}

Şimdi tüm numaralandırmaların anahtar değer çiftlerinin ve açıklamalarının bir listesine sahip olacaksınız. Öyleyse, bunu bir birleşik giriş kutusu için bir veri kaynağı olarak atayalım.

var comboBox = new ComboBox();
comboBox.ValueMember = "Key"
comboBox.DisplayMember = "Value";
comboBox.DataSource = EnumUtilities.ToList<Separator>();

comboBox.SelectedIndexChanged += (sender, e) =>
{
    var selectedEnum = (Separator)comboBox.SelectedValue;
    MessageBox.Show(selectedEnum.ToString());
}

Kullanıcı, numaralandırmanın tüm dize temsillerini görür ve kodunuzun içinde istediğiniz enum değerini alırsınız.


0

Numaralandırmayı dize türü olarak tanımlayamayız. Bir numaralandırma için onaylanmış türler bayt, sbyte, short, ushort, int, uint, long veya ulong'dur.

Numaralandırma hakkında daha fazla ayrıntıya ihtiyacınız varsa, lütfen aşağıdaki bağlantıyı takip edin, bu bağlantı numaralandırmayı anlamanıza yardımcı olacaktır. Numaralandırma

@hayalhanemersin


0

Benim için çalışıyor ..

   public class ShapeTypes
    {
        private ShapeTypes() { }
        public static string OVAL
        {
            get
            {
                return "ov";
            }
            private set { }
        }

        public static string SQUARE
        {
            get
            {
                return "sq";
            }
            private set { }
        }

        public static string RECTANGLE
        {
            get
            {
                return "rec";
            }
            private set { }
        }
    }

0

Son zamanlarda yapmaya başladığım şey Tuples kullanmak

public static (string Fox, string Rabbit, string Horse) Animals = ("Fox", "Rabbit", "Horse");
...
public static (string Comma, string Tab, string Space) SeparatorChars = (",", "\t", " ");

-1

Numaralandırma Sınıfı

 public sealed class GenericDateTimeFormatType
    {

        public static readonly GenericDateTimeFormatType Format1 = new GenericDateTimeFormatType("dd-MM-YYYY");
        public static readonly GenericDateTimeFormatType Format2 = new GenericDateTimeFormatType("dd-MMM-YYYY");

        private GenericDateTimeFormatType(string Format)
        {
            _Value = Format;
        }

        public string _Value { get; private set; }
    }

Enumaration Consuption

public static void Main()
{
       Country A = new Country();

       A.DefaultDateFormat = GenericDateTimeFormatType.Format1;

      Console.ReadLine();
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.