Mengaitkan enum dengan string dalam C #

363

Saya tahu yang berikut ini tidak mungkin karena tipe Pencacahan harus berupa int

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

Dari basis data saya, saya mendapatkan bidang dengan kode yang tidak komprehensif ( OEMdan CMBs). Saya ingin membuat bidang ini menjadi enumatau sesuatu yang bisa dimengerti. Karena jika targetnya mudah dibaca, solusinya harus singkat.

Opsi apa lagi yang saya miliki?

Boris Callens
sumber
kemungkinan duplikat dari Enum ToString
nawfal
12
Saya tidak yakin mengapa sebagian besar jawaban tidak hanya menggunakan "string const" dan sebaliknya mereka membuat kelas khusus.
CTS_AE
1
Anda mungkin tidak bisa menggunakan string, tetapi Anda bisa menggunakan karakter saja. Itu opsi jika Anda bisa menggunakan nilai huruf tunggal.
T. Sar
1
Benar-benar bingung mengapa solusi yang diusulkan di atas oleh CTS_AE bahkan tidak ada dalam tiga jawaban teratas.
Sinjai
@Sinjai Pengelompokan eksplisit nilai-nilai terkait akan lebih besar daripada penalti hilangnya kinerja yang tidak terlihat, terutama dalam API atau komponen yang dapat digunakan kembali.
27

Jawaban:

404

Saya suka menggunakan properti di kelas daripada metode, karena mereka terlihat lebih seperti enum.

Berikut ini contoh untuk seorang Logger:

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

Lewati nilai string tipe-aman sebagai parameter:

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

Pemakaian:

Logger.Write("This is almost like an enum.", LogCategory.Info);
Bahkan Mien
sumber
4
Hanya sisi buruk yang bisa saya kemukakan adalah bahwa itu akan sedikit lebih lambat, tetapi dalam banyak kasus hal ini bisa diabaikan. Dan itu tidak akan memiliki perilaku yang sama persis di editor. EG: beralih dari yang ini tidak akan secara otomatis mengisi kasing untuk setiap kemungkinan. Selain hal-hal kecil itu, saya pikir ini mungkin solusi yang agak sederhana.
Boris Callens
3
Dan mudah untuk menggunakan Kamus <LogCategory, Action / Func> sebagai saklar. :)
Arnis Lapsa
4
@ArnisL. Ini tidak cukup untuk berfungsi sebagai kunci, Anda harus mengganti Equals () dan GetHashCode (), dan Anda ingin menjadikan setter properti Value pribadi. Tetap saja, ini bukan enum.
Dave Van den Eynde
21
Untuk penggunaan saya sendiri, saya memperluas konsep ini, mengganti ToStringmetode untuk kembali Value. Dan kemudian memberikan operator pemeran implisit ke dan dari string. public static implicit operator String(LogCategory category) { return Value; }.
Zarepheth
6
Bagaimana dengan menggunakan ini dalam switch case?
David
175

Anda juga dapat menggunakan model ekstensi:

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

Kelas Ekstensi Anda

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

pemakaian:

MyEnum myLocal = MyEnum.V1;
print(myLocal.ToDescriptionString());
Glennular
sumber
3
Lihat juga stackoverflow.com/questions/4367723/... untuk ekstensi lain dan dari string ke enum melalui deskripsi.
Dave
15
Saya tidak dapat tidak berpikir bahwa mencerminkan enum setiap kali Anda ingin menampilkan teks terdengar agak menyakitkan dari perspektif kinerja!
Liath
4
@Liath - `.ToString ()` sudah menggunakan refleksi, jadi Anda tidak benar-benar kehilangan apa pun dengan pendekatan ini, dan mendapatkan kesiapan
James King
1
Bisakah Anda membuat Extension generik sehingga diterapkan secara otomatis ke semua Enums?
erosebe
3
Untuk membuat generik, gunakan public static string ToDescriptionString(this Enum ...yaitu tanpa mengetik secara eksplisit ke MyEnum.
LeeCambl
100

Bagaimana kalau menggunakan kelas statis dengan konstanta?

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
sumber
9
Sepakat. Saya mengalami kesulitan melihat tujuan di balik solusi yang lebih kompleks, kecuali mungkin untuk dapat mengalihkan "enum" yang dihasilkan.
fakeleft
@fakeleft Anda tidak dapat menggunakan tipe kelas statis dengan tipe generik (templat), dan mungkin batasan lainnya, saya pikir itu sebabnya orang lebih suka solusi "lebih kompleks".
eselk
2
Konstanta perlu internal atau publik agar ini dapat berfungsi
arviman
46
Tipe statis tidak dapat digunakan sebagai parameter.
Pedro Moreira
2
Seperti @PedroMoreira tunjukkan, Anda tidak bisa lulus GroupTypessebagai tipe argumen karena ini adalah kelas statis. Itulah masalah yang dipecahkan oleh jawaban Bahkan Mien. Dalam hal ini Anda malah harus memiliki void DoSomething(string groupType), yang berarti groupTypedapat memiliki nilai string apa pun , bahkan nilai yang tidak Anda harapkan, yang berarti Anda harus siap untuk jenis yang tidak valid dan memutuskan bagaimana menanganinya (mis. dengan melemparkan pengecualian). Bahkan jawaban Mien memecahkan itu dengan membatasi jumlah input yang valid ke opsi yang ditentukan oleh LogCategorykelas.
Pharap
30

Anda bisa menambahkan atribut ke item dalam penghitungan dan kemudian menggunakan refleksi untuk mendapatkan nilai dari atribut.

Anda harus menggunakan specifier "bidang" untuk menerapkan atribut, seperti:

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

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

Anda kemudian akan merefleksikan bidang statis dari jenis enum (dalam hal ini GroupTypes) dan mendapatkan DescriptionAttributenilai yang Anda cari menggunakan refleksi:

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

Saya memilih untuk mengembalikan DescriptionAttributesendiri di atas, jika Anda ingin dapat menentukan apakah atribut tersebut diterapkan atau tidak.

casperOne
sumber
Walaupun saya akan mengingat ini untuk situasi yang lebih kompleks, ini agak rumit untuk situasi dengan tingkat kerumitan dari apa yang saya nyatakan dalam OP
Boris Callens
26

Anda dapat melakukannya dengan sangat mudah sebenarnya. Gunakan kode berikut.

enum GroupTypes
{
   OEM,
   CMB
};

Kemudian ketika Anda ingin mendapatkan nilai string dari setiap elemen enum cukup gunakan baris kode berikut.

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

Saya telah menggunakan metode ini dengan sukses di masa lalu, dan saya juga menggunakan kelas konstanta untuk menahan konstanta string, keduanya bekerja dengan cukup baik, tetapi saya cenderung lebih suka ini.

Arthur C
sumber
Saya sedang memikirkan hal yang sama, tetapi harus ada yang menangkap ini ... Kalau tidak, saya akan curiga lebih banyak orang akan menyarankan ini (Mungkin saya hanya paranoid).
Matthijs Wessels
Satu-satunya hasil tangkapan yang saya sadari adalah bahwa saya percaya ini menggunakan refleksi untuk mengetahui string. Akibatnya jika saya hanya mencari solusi untuk melacak string konstan, maka saya biasanya akan menggunakan kelas untuk menyimpan sebagian besar string konstan saya. Namun jika saya memiliki situasi di mana Enum adalah solusi yang tepat (terlepas dari mendapatkan string deskriptif tentang elemen Enum saya), maka daripada memiliki string tambahan yang melayang di suatu tempat untuk mengelola saya hanya menggunakan nilai enum seperti yang dijelaskan.
Arthur C
+1 Ini ADALAH jawaban terbaik dan termudah, dan juga memiliki suara tinggi di sini untuk membuktikannya. Satu-satunya waktu lebih baik menggunakan model ekstensi adalah ketika Anda membutuhkan spasi dalam teks (lebih detail di sini )
SharpC
14
Tidak, ini hanya mendapatkan nama nilai enum, bukan menetapkan string ke nilai enum. Tujuan OP adalah memiliki string yang berbeda dari nilai enum misalnya: TheGroup = "OEM", TheOtherGroup = "CMB".
Tim Autin
3
Saya setuju dengan komentar @ Tim, ini bukan yang OP coba lakukan. Jika Anda bertanya-tanya apa itu use case, pertimbangkan situasi di mana perangkat mengambil string sebagai perintah, tetapi perlu ada versi "yang dapat dibaca manusia" dari perintah juga. Saya membutuhkan ini untuk mengaitkan sesuatu seperti "Perbarui Firmware" dengan perintah "UPDATEFW".
JYelton
20

Coba tambahkan konstanta ke kelas statis. Anda tidak berakhir dengan suatu Tipe, tetapi Anda akan memiliki konstanta yang dapat dibaca dan diatur:

public static class GroupTypes {

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

}
darasd
sumber
3
Sulit untuk beralih dari kode kembali ke nama deskriptif. Anda harus menggunakan refleksi atas semua bidang const untuk mencari kecocokan.
andleer
1
@andleer Saya tidak mengerti masalah Anda. Ini solusi yang saya gunakan.
VSO
Ya inilah sebenarnya yang saya inginkan. Dan ini adalah solusi paling ringkas / elegan yang saya lihat, sama seperti jika saya mendefinisikan nilai w / int enumerasi - tetapi dengan nilai string. 100% sempurna.
Chad
3
Masalahnya adalah ini tidak berfungsi sebagai Enum dalam arti bahwa kita tidak akan memiliki tipe terpisah dengan daftar nilai yang terbatas. Fungsi yang mengharapkan ini dapat digunakan dengan string bentuk bebas yang rentan kesalahan.
Juan Martinez
14

Buat enum kedua, untuk DB Anda yang berisi yang berikut ini:

enum DBGroupTypes
{
    OEM = 0,
    CMB = 1
}

Sekarang, Anda bisa menggunakan Enum.Parse untuk mengambil nilai DBGroupTypes yang benar dari string "OEM" dan "CMB". Anda kemudian dapat mengonversikannya ke int dan mengambil nilai yang benar dari penghitungan yang tepat yang ingin Anda gunakan lebih lanjut dalam model Anda.

Dave Van den Eynde
sumber
Ini tampaknya menjadi langkah ekstra dalam proses, mengapa tidak satu kelas yang menangani semuanya?
C. Ross
11
Berbeda dengan menggunakan atribut dan refleksi?
Dave Van den Eynde
13

Gunakan kelas.

Sunting: Contoh yang lebih baik

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.");
    }
}
C. Ross
sumber
1
Sulit untuk beralih dari kode kembali ke nama deskriptif. Anda harus menggunakan refleksi atas semua bidang const untuk mencari kecocokan.
andleer
1
Saya mengerti maksud Anda. Saya akan mengunggah versi yang bekerja secara normal nanti, tetapi saya akui itu cukup berat.
C. Ross
Versi saya berdasarkan pada solusi C. Ross stackoverflow.com/a/48441114/3862615
Roman M
7

Cara lain untuk mengatasi masalah, adalah memiliki enum dan array string yang akan memetakan nilai enum dengan daftar string:

public enum GroupTypes
{
    TheGroup  = 0,
    TheOtherGroup 
}

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

Anda dapat menggunakannya seperti ini:

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

Ini akan meminta CMB

PROS:

  1. Kode mudah dan bersih.
  2. Kinerja Tinggi (khusus dibandingkan dengan pendekatan yang menggunakan kelas)

CONS:

  1. Cenderung mengacaukan daftar saat mengeditnya, tetapi tidak apa-apa untuk daftar pendek.
Luis Orantes
sumber
6

Berikut adalah metode ekstensi yang saya gunakan untuk mendapatkan nilai enum sebagai string. Pertama di sini adalah enum.

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

Atribut Deskripsi berasal dari System.ComponentModel.

Dan inilah metode ekstensi saya:

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

Sekarang, Anda dapat mengakses enum sebagai nilai string menggunakan kode berikut:

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

Sudahkah Anda mempertimbangkan tabel pencarian menggunakan Kamus?

enum GroupTypes
{
    TheGroup,
    TheOtherGroup
}

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

Anda kemudian dapat menggunakan GroupTypeLookup.TryGetValue () untuk mencari string ketika Anda membacanya.

Jim Mischel
sumber
Bagaimana Anda dengan mudah mendapatkan kunci untuk nilai yang diberikan?
eglasius
Pertanyaannya tidak meminta sebaliknya. Tapi itu akan cukup sederhana untuk membangun kamus lain yang sebaliknya. Yaitu, Kamus <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
sumber
3

C # tidak mendukung string yang disebutkan, tetapi untuk sebagian besar situasi Anda dapat menggunakan Daftar atau Kamus untuk mendapatkan efek yang diinginkan.

Misalnya untuk mencetak hasil lulus / gagal:

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

Saya akan membuatnya menjadi kelas yang menghindari enum sama sekali. Dan kemudian dengan menggunakan typehandler Anda bisa membuat objek saat Anda mengambilnya dari db.

YAITU:

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
sumber
2

Saya hanya akan membuat kamus dan menggunakan kode sebagai kuncinya.

Sunting: Untuk mengatasi komentar tentang melakukan pencarian terbalik (menemukan kunci), ini tidak akan sangat efisien. Jika ini perlu, saya akan menulis kelas baru untuk mengatasinya.

jhale
sumber
Anda juga dapat dengan mudah mengambil kunci untuk nilai yang diberikan?
eglasius
Untuk C.Ross - Saya tidak yakin apa yang Anda maksud. Anda dapat membaca nilai dari db dan secara dinamis mengisi kamus.
jhale
2

Pertanyaan pertama saya - Apakah Anda memiliki akses ke Database itu sendiri? Ini harus dinormalisasi dalam database, idealnya, jika tidak, solusi apa pun akan rentan terhadap kesalahan. Dalam pengalaman saya, bidang data yang penuh dengan "OEM" dan "CMB" cenderung berakhir dengan hal-hal seperti "oem" dan 'data sampah' lainnya bercampur seiring waktu .... Jika Anda dapat menormalkannya, Anda dapat menggunakan kunci di tabel yang berisi elemen sebagai Enum Anda, dan Anda selesai, dengan struktur yang jauh lebih bersih.

Jika itu tidak tersedia, saya akan membuat Enum Anda, dan membuat kelas untuk mengurai string Anda ke dalam Enum untuk Anda. Ini setidaknya akan memberi Anda beberapa fleksibilitas dalam menangani entri yang tidak standar dan jauh lebih fleksibel untuk menjebak atau menangani kesalahan daripada melakukan pemecahan masalah menggunakan Enum.Parse / Reflection / dll. Kamus akan berfungsi, tetapi bisa rusak jika Anda memiliki masalah kasus, dll.

Saya akan merekomendasikan menulis kelas sehingga Anda dapat melakukannya:

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

Ini menjaga sebagian besar keterbacaan Anda tanpa harus mengubah DB.

Reed Copsey
sumber
2

Jika saya mengerti dengan benar, Anda perlu konversi dari string ke 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");

Anda dapat membuatnya lebih mewah dengan obat generik untuk tipe enum jika diinginkan.

dmihailescu
sumber
2

Di VS 2015, Anda dapat menggunakan nama

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

Pemakaian:

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

Tweak kecil untuk metode Ekstensi Glennular, sehingga Anda dapat menggunakan ekstensi pada hal lain selain dari 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;
        }
    }
}

Atau Menggunakan 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
sumber
2

Mengikuti jawaban @Even Mien saya telah mencoba untuk melangkah lebih jauh dan membuatnya menjadi Generic, saya sepertinya hampir ada tetapi satu case masih menolak dan saya mungkin dapat menyederhanakan kode saya sedikit.
Saya mempostingnya di sini jika ada yang melihat bagaimana saya dapat meningkatkan dan terutama membuatnya berfungsi karena saya tidak dapat menetapkannya dari string

Sejauh ini saya memiliki hasil sebagai berikut:

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

Di mana keajaiban terjadi:

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



}

Saya hanya kemudian harus menyatakan ini untuk enum saya:

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
sumber
Terima kasih atas pekerjaan yang luar biasa ini, saya sudah mencari pendekatan seperti itu untuk waktu yang lama. Saya pikir Anda harus mendapatkan 1000 poin untuk ini
user3492977
Oh terima kasih atas komentar ini, dan terima kasih telah mengingatkan saya tentang ini, saya belum pernah menggunakan c # selama dua tahun ketika saya menulis sedikit kode ini, saya harus segera kembali ke sana!
Lomithrani
@ user3492977 akhirnya saya kembali ke sana dan membuatnya berfungsi penuh, saya masih ragu apakah itu ide bagus atau hal yang tidak berguna: D stackoverflow.com/questions/62043138/…
Lomithrani
2

Baru di .Net Core 3.0 / C # 8.0 (jika lingkungan kerja Anda memungkinkan Anda untuk memutakhirkan proyek Anda) adalah pernyataan sakelar tangan pendek yang terlihat agak enum-ish. Pada akhirnya, itu adalah pernyataan beralih membosankan lama yang sama yang telah kami gunakan selama bertahun-tahun.

Satu-satunya perbedaan nyata di sini adalah bahwa pergantian pernyataan mendapat setelan baru.

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

Anda akan melihat bahwa kode di atas yang saya salin dari sini , sebenarnya menggunakan enum sebagai param.

Ini bukan apa yang Anda inginkan (dan percayalah, saya menginginkan sesuatu yang mirip dengan apa yang diminta OP untuk waktu yang lama), tetapi saya benar-benar merasa ini semacam cabang zaitun dari MS. JMO.

Semoga ini bisa membantu seseorang!

pengguna3180664
sumber
2

Saya menggunakan struktur seperti disinggung dalam jawaban sebelumnya, tetapi menghilangkan kompleksitas. Bagi saya, ini paling seperti membuat enumerasi string. Ini digunakan dengan cara yang sama bahwa enumerasi digunakan.

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

Contoh penggunaan:

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

Saya bahkan menerapkan beberapa enum seperti yang disarankan oleh @Even (via class Xdan public static Xanggota), hanya untuk mengetahui kemudian bahwa hari ini, mulai. Net 4.5, ada metode yang tepat ToString() .

Sekarang saya menerapkan kembali semuanya ke enum.

bohdan_trotsenko
sumber
1

Ini adalah cara untuk menggunakannya sebagai parameter yang sangat diketik atau sebagai string :

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
sumber
1

Anda dapat menggunakan dua enum. Satu untuk database dan yang lainnya untuk dibaca.

Anda hanya perlu memastikan mereka tetap sinkron, yang tampaknya seperti biaya yang kecil. Anda tidak harus mengatur nilai, cukup mengatur posisi yang sama, tetapi pengaturan nilai membuatnya sangat jelas bahwa dua enum terkait dan mencegah kesalahan mengatur ulang anggota enum. Dan komentar membuat kru pemeliharaan tahu ini terkait dan harus tetap sinkron.

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

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

Untuk menggunakannya, Anda cukup mengonversi ke kode terlebih dahulu:

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

Kemudian jika Anda ingin membuatnya lebih nyaman, Anda dapat menambahkan fungsi ekstensi yang hanya berfungsi untuk enum jenis ini:

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

dan kemudian Anda bisa melakukan:

GroupTypes myGroupType = GroupTypes.TheGroup;
string valueToSaveIntoDatabase = myGroupType.ToString();
toddmo
sumber
Itu adalah praktik yang buruk: Dengan ketergantungan, enumperubahan nilai yang dimaksudkan pada satu dapat secara tidak sengaja mengacaukan yang lain.
Lorenz Lo Sauer
1

Saya pada dasarnya mencari jawaban Refleksi oleh @ArthurC

Hanya untuk sedikit memperluas jawabannya, Anda dapat membuatnya lebih baik dengan memiliki fungsi generik:

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

Maka Anda bisa membungkus apa pun yang Anda miliki

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

atau

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

Diambil dari @EvenMien dan ditambahkan di beberapa komentar. (Juga untuk kasus penggunaan saya sendiri)

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

Menambahkan kelas ini

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

Karya ini digunakan CallerMemberNameuntuk meminimalkan pengkodean

Menggunakan:

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

Menguji:

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

keluaran:

ScannerDefaultFlashLight
Scanner1dCodes
nyconing
sumber
0

Berdasarkan pendapat lain, inilah yang saya kemukakan. Pendekatan ini menghindari keharusan mengetik. Nilai di mana Anda ingin mendapatkan nilai konstan.

Saya memiliki kelas dasar untuk semua string enum seperti ini:

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 seperti ini:

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

Dan string enum aktual akan menjadi seperti ini:

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

Dan dengan ini, Anda bisa menggunakan Color.Red untuk membuat serial ke json tanpa menggunakan properti Value

ayepiz
sumber
0

Saya tidak membutuhkan sesuatu yang kuat seperti menyimpan string dalam atribut. Saya hanya perlu mengubah sesuatu seperti MyEnum.BillEveryWeekmenjadi "tagihan setiap minggu" atau MyEnum.UseLegacySystemmenjadi "gunakan sistem warisan" - pada dasarnya membagi enum dengan casing untanya menjadi kata-kata kecil individual.

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() output "gunakan sistem warisan"

Jika Anda memiliki beberapa flag yang ditetapkan, itu akan mengubahnya menjadi bahasa Inggris biasa (dibatasi koma kecuali "dan" di tempat koma terakhir).

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

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