Ubah string menjadi enum di C #


894

Apa cara terbaik untuk mengonversi string ke nilai enumerasi dalam C #?

Saya memiliki tag pilih HTML yang berisi nilai-nilai enumerasi. Ketika halaman diposting, saya ingin mengambil nilai (yang akan berupa string) dan mengubahnya menjadi nilai enumerasi.

Di dunia yang ideal, saya bisa melakukan sesuatu seperti ini:

StatusEnum MyStatus = StatusEnum.Parse("Active");

tapi itu bukan kode yang valid.

Jawaban:


1510

Di .NET Core dan .NET> 4 ada metode parse generik :

Enum.TryParse("Active", out StatusEnum myStatus);

Ini juga termasuk outvariabel inline baru C # 7 , jadi ini melakukan try-parse, konversi ke tipe enum eksplisit dan menginisialisasi + mengisi myStatusvariabel.

Jika Anda memiliki akses ke C # 7 dan .NET terbaru, ini adalah cara terbaik.

Jawaban Asli

Di .NET agak jelek (sampai 4 atau lebih):

StatusEnum MyStatus = (StatusEnum) Enum.Parse(typeof(StatusEnum), "Active", true);

Saya cenderung menyederhanakan ini dengan:

public static T ParseEnum<T>(string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}

Maka saya bisa melakukan:

StatusEnum MyStatus = EnumUtil.ParseEnum<StatusEnum>("Active");

Salah satu opsi yang disarankan dalam komentar adalah menambahkan ekstensi, yang cukup sederhana:

public static T ToEnum<T>(this string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}

StatusEnum MyStatus = "Active".ToEnum<StatusEnum>();

Terakhir, Anda mungkin ingin memiliki enum default untuk digunakan jika string tidak dapat diuraikan:

public static T ToEnum<T>(this string value, T defaultValue) 
{
    if (string.IsNullOrEmpty(value))
    {
        return defaultValue;
    }

    T result;
    return Enum.TryParse<T>(value, true, out result) ? result : defaultValue;
}

Yang membuat ini panggilan:

StatusEnum MyStatus = "Active".ToEnum(StatusEnum.None);

Namun, saya akan berhati-hati menambahkan metode ekstensi seperti ini stringsebagai (tanpa kontrol namespace) itu akan muncul di semua contoh stringapakah mereka memegang enum atau tidak (jadi 1234.ToString().ToEnum(StatusEnum.None)akan valid tetapi tidak masuk akal). Sering kali yang terbaik adalah menghindari kekacauan kelas inti Microsoft dengan metode tambahan yang hanya berlaku dalam konteks yang sangat spesifik kecuali jika seluruh tim pengembangan Anda memiliki pemahaman yang sangat baik tentang apa yang dilakukan ekstensi tersebut.


17
Jika performace penting (yang selalu ada) chk jawaban yang diberikan oleh Mckenzieg1 di bawah ini: stackoverflow.com/questions/16100/…
Nash

28
@avinashr benar tentang jawaban @ McKenzieG1, tetapi itu tidak SELALU penting. Misalnya itu akan menjadi optimasi mikro sia-sia untuk khawatir tentang enum parsing jika Anda membuat panggilan DB untuk setiap parse.
Keith

4
@ HM Saya tidak berpikir ekstensi sesuai di sini - itu sedikit kasus khusus dan ekstensi akan berlaku untuk setiap string. Jika Anda benar-benar ingin melakukannya meskipun itu akan menjadi perubahan sepele.
Keith

7
Bagaimana dengan Enum.TryParse?
Elaine

15
sangat bagus. Anda memerlukan T: struct dalam contoh terakhir Anda.
bbrik

331

Gunakan Enum.TryParse<T>(String, T)(≥ .NET 4.0):

StatusEnum myStatus;
Enum.TryParse("Active", out myStatus);

Itu dapat disederhanakan lebih jauh dengan tipe parameter C # 7.0 di sebaris :

Enum.TryParse("Active", out StatusEnum myStatus);

45
Tambahkan parameter boolean tengah untuk sensitivitas huruf dan sejauh ini ini adalah solusi teraman dan paling elegan.
DanM7

18
Ayo, berapa banyak dari Anda yang menerapkan jawaban terpilih itu dari 2008 untuk hanya menggulir ke bawah dan menemukan ini adalah jawaban yang lebih baik (modern).
TEK

@TEK Saya sebenarnya lebih suka jawaban 2008.
Zero3

Saya tidak mengerti. Parsemelempar pengecualian penjelas untuk apa yang salah dengan konversi (nilainya null, kosong atau tidak ada konstanta enum yang sesuai), yang jauh lebih baik daripada TryParsenilai pengembalian boolean (yang menekan kesalahan konkret)
yair

2
Enum.TryParse <T> (String, T) cacat saat mengurai string integer. Misalnya, kode ini akan berhasil menguraikan string yang tidak masuk akal sebagai enum yang tidak masuk akal: var result = Enum.TryParse<System.DayOfWeek>("55", out var parsedEnum);
Mass Dot Net

196

Perhatikan bahwa kinerja Enum.Parse()mengerikan, karena dilaksanakan melalui refleksi. (Hal yang sama berlaku untuk Enum.ToStringyang sebaliknya.)

Jika Anda perlu mengonversi string ke Enums dalam kode yang peka terhadap kinerja, taruhan terbaik Anda adalah membuat Dictionary<String,YourEnum>saat startup dan menggunakannya untuk melakukan konversi.


10
Saya telah mengukur 3ms untuk mengonversi string menjadi Enum pada proses pertama, pada komputer desktop. (Hanya untuk menggambarkan tingkat awfullness).
Matthieu Charbonnier

12
Wow 3ms adalah pesanan yang luar biasa mengerikan
John Stock

1
dapatkah Anda menambahkan contoh kode di sekitar ini, jadi kami mendapatkan gagasan tentang cara mengganti dan menggunakan
transformer

Jika aplikasi Anda digunakan oleh 1 juta orang => itu menambahkan hingga 50 jam kehidupan manusia yang Anda konsumsi :) Pada satu penggunaan halaman. : P
Cătălin Rădoi


31

Anda dapat menggunakan metode ekstensi sekarang:

public static T ToEnum<T>(this string value, bool ignoreCase = true)
{
    return (T) Enum.Parse(typeof (T), value, ignoreCase);
}

Dan Anda dapat memanggil mereka dengan kode di bawah ini (di sini, FilterTypeadalah tipe enum):

FilterType filterType = type.ToEnum<FilterType>();

1
Saya telah memperbarui ini untuk mengambil nilai sebagai objek dan melemparkannya ke string di dalam metode ini. Dengan cara ini saya bisa mengambil nilai int. TooNn bukan string saja.
RealSollyM

2
@SollyM Saya akan mengatakan itu adalah ide yang mengerikan karena kemudian metode ekstensi ini akan berlaku untuk semua jenis objek. Dua metode ekstensi, satu untuk string dan satu untuk int, akan lebih bersih dan lebih aman menurut saya.
Svish

@Vish, itu benar. Satu-satunya alasan saya melakukan ini adalah karena kode kami hanya digunakan secara internal dan saya ingin menghindari penulisan 2 ekstensi. Dan karena satu-satunya waktu kita mengonversi ke Enum adalah dengan string atau int, saya tidak melihatnya menjadi masalah sebaliknya.
RealSollyM

3
@SollyM Internal atau tidak, saya masih mempertahankan dan menggunakan kode saya: PI akan terganggu jika saya mendapatkan ToEnum di setiap menu intellisense, dan seperti yang Anda katakan, karena satu-satunya saat Anda mengonversi ke enum adalah dari string atau int, Anda bisa sangat yakin Anda hanya perlu dua metode itu. Dan dua metode tidak lebih dari satu, terutama ketika mereka ini kecil dan jenis utilitas: P
Svish

20
object Enum.Parse(System.Type enumType, string value, bool ignoreCase);

Jadi jika Anda memiliki mood enum bernama itu akan terlihat seperti ini:

   enum Mood
   {
      Angry,
      Happy,
      Sad
   } 

   // ...
   Mood m = (Mood) Enum.Parse(typeof(Mood), "Happy", true);
   Console.WriteLine("My mood is: {0}", m.ToString());

18

WASPADALAH:

enum Example
{
    One = 1,
    Two = 2,
    Three = 3
}

Enum.(Try)Parse() menerima beberapa argumen yang dipisahkan koma, dan menggabungkannya dengan biner 'atau'| . Anda tidak dapat menonaktifkan ini dan menurut saya Anda hampir tidak pernah menginginkannya.

var x = Enum.Parse("One,Two"); // x is now Three

Bahkan jika Threetidak didefinisikan, xmasih akan mendapatkan nilai int 3. Itu bahkan lebih buruk: Enum.Parse () dapat memberi Anda nilai yang bahkan tidak ditentukan untuk enum!

Saya tidak ingin mengalami konsekuensi dari pengguna, mau tidak mau, memicu perilaku ini.

Selain itu, seperti yang disebutkan oleh orang lain, kinerja kurang dari ideal untuk enum besar, yaitu linier dalam jumlah nilai yang mungkin.

Saya menyarankan yang berikut ini:

    public static bool TryParse<T>(string value, out T result)
        where T : struct
    {
        var cacheKey = "Enum_" + typeof(T).FullName;

        // [Use MemoryCache to retrieve or create&store a dictionary for this enum, permanently or temporarily.
        // [Implementation off-topic.]
        var enumDictionary = CacheHelper.GetCacheItem(cacheKey, CreateEnumDictionary<T>, EnumCacheExpiration);

        return enumDictionary.TryGetValue(value.Trim(), out result);
    }

    private static Dictionary<string, T> CreateEnumDictionary<T>()
    {
        return Enum.GetValues(typeof(T))
            .Cast<T>()
            .ToDictionary(value => value.ToString(), value => value, StringComparer.OrdinalIgnoreCase);
    }

4
Padahal ini sangat berguna untuk mengetahui hal itu Enum.(Try)Parse accepts multiple, comma-separated arguments, and combines them with binary 'or'. Berarti Anda dapat mengatur nilai enum Anda sebagai kekuatan 2 dan Anda memiliki cara yang sangat mudah untuk mem-parsing beberapa flag boolean, mis. "GunakanSSL, NoRetries, Sync". Bahkan mungkin memang dirancang untuk apa.
pcdev

16

Enum.Parse adalah teman Anda:

StatusEnum MyStatus = (StatusEnum)Enum.Parse(typeof(StatusEnum), "Active");

13

Anda dapat memperluas jawaban yang diterima dengan nilai default untuk menghindari pengecualian:

public static T ParseEnum<T>(string value, T defaultValue) where T : struct
{
    try
    {
        T enumValue;
        if (!Enum.TryParse(value, true, out enumValue))
        {
            return defaultValue;
        }
        return enumValue;
    }
    catch (Exception)
    {
        return defaultValue;
    }
}

Maka Anda menyebutnya seperti:

StatusEnum MyStatus = EnumUtil.ParseEnum("Active", StatusEnum.None);

Jika nilai default bukan enum, Enum.TryParse akan gagal dan melempar pengecualian yang ditangkap.

Setelah bertahun-tahun menggunakan fungsi ini dalam kode kami di banyak tempat, mungkin ada baiknya menambahkan informasi bahwa operasi ini membutuhkan kinerja!


Saya tidak suka nilai default. Ini dapat menyebabkan hasil yang tidak terduga.
Daniël Tulp

5
kapan ini akan pernah membuat pengecualian?
andleer

@andleer jika nilai enum tidak cocok dengan tipe enum yang sama dengan nilai default
Nelly

@Nelly Kode lama di sini tapi defaultValuedan metode tipe pengembalian keduanya bertipe T. Jika jenisnya berbeda, Anda akan menerima kesalahan waktu kompilasi: "tidak dapat mengonversi dari 'ConsoleApp1.Size' ke 'ConsoleApp1.Color'" atau apa pun jenisnya.
andleer

@andleer, maaf jawaban terakhir saya untuk Anda tidak benar. Ada kemungkinan bahwa metode ini melempar Syste.ArgumentException dalam kasus seseorang memanggil fungsi ini dengan nilai default yang bukan tipe enum. Dengan c # 7.0 saya tidak bisa membuat klausa dimana T: Enum. Itulah mengapa saya menangkap kemungkinan ini dengan try catch.
Nelly

8

Kami tidak dapat menerima input yang benar-benar valid, dan menggunakan variasi jawaban @ Keith ini:

public static TEnum ParseEnum<TEnum>(string value) where TEnum : struct
{
    TEnum tmp; 
    if (!Enum.TryParse<TEnum>(value, true, out tmp))
    {
        tmp = new TEnum();
    }
    return tmp;
}

7
// str.ToEnum<EnumType>()
T static ToEnum<T>(this string str) 
{ 
    return (T) Enum.Parse(typeof(T), str);
}

5

Mengurai string ke TEnum tanpa mencoba / menangkap dan tanpa metode TryParse () dari .NET 4.5

/// <summary>
/// Parses string to TEnum without try/catch and .NET 4.5 TryParse()
/// </summary>
public static bool TryParseToEnum<TEnum>(string probablyEnumAsString_, out TEnum enumValue_) where TEnum : struct
{
    enumValue_ = (TEnum)Enum.GetValues(typeof(TEnum)).GetValue(0);
    if(!Enum.IsDefined(typeof(TEnum), probablyEnumAsString_))
        return false;

    enumValue_ = (TEnum) Enum.Parse(typeof(TEnum), probablyEnumAsString_);
    return true;
}

1
Apakah perlu membuat deskripsi jika kode sudah berisi deskripsi? Oke, saya lakukan ini :)
jite.gs

3

Kode super sederhana menggunakan TryParse:

var value = "Active";

StatusEnum status;
if (!Enum.TryParse<StatusEnum>(value, out status))
    status = StatusEnum.Unknown;

2

Saya suka solusi metode ekstensi ..

namespace System
{
    public static class StringExtensions
    {

        public static bool TryParseAsEnum<T>(this string value, out T output) where T : struct
        {
            T result;

            var isEnum = Enum.TryParse(value, out result);

            output = isEnum ? result : default(T);

            return isEnum;
        }
    }
}

Di sini di bawah ini implementasi saya dengan tes.

using static Microsoft.VisualStudio.TestTools.UnitTesting.Assert;
using static System.Console;

private enum Countries
    {
        NorthAmerica,
        Europe,
        Rusia,
        Brasil,
        China,
        Asia,
        Australia
    }

   [TestMethod]
        public void StringExtensions_On_TryParseAsEnum()
        {
            var countryName = "Rusia";

            Countries country;
            var isCountry = countryName.TryParseAsEnum(out country);

            WriteLine(country);

            IsTrue(isCountry);
            AreEqual(Countries.Rusia, country);

            countryName = "Don't exist";

            isCountry = countryName.TryParseAsEnum(out country);

            WriteLine(country);

            IsFalse(isCountry);
            AreEqual(Countries.NorthAmerica, country); // the 1rst one in the enumeration
        }

1
public static T ParseEnum<T>(string value)            //function declaration  
{
    return (T) Enum.Parse(typeof(T), value);
}

Importance imp = EnumUtil.ParseEnum<Importance>("Active");   //function call

==================== Program Lengkap ====================

using System;

class Program
{
    enum PetType
    {
    None,
    Cat = 1,
    Dog = 2
    }

    static void Main()
    {

    // Possible user input:
    string value = "Dog";

    // Try to convert the string to an enum:
    PetType pet = (PetType)Enum.Parse(typeof(PetType), value);

    // See if the conversion succeeded:
    if (pet == PetType.Dog)
    {
        Console.WriteLine("Equals dog.");
    }
    }
}
-------------
Output

Equals dog.

1

Saya menggunakan kelas (versi yang sangat diketik dari Enum dengan parsing dan peningkatan kinerja). Saya menemukannya di GitHub, dan seharusnya berfungsi juga untuk .NET 3.5. Ini memiliki beberapa overhead memori karena buffer kamus.

StatusEnum MyStatus = Enum<StatusEnum>.Parse("Active");

Blogpost adalah Enums - Sintaks yang lebih baik, peningkatan kinerja dan TryParse di NET 3.5 .

Dan kode: https://github.com/damieng/DamienGKit/blob/master/CSharp/DamienG.Library/System/EnumT.cs


1

Untuk kinerja, ini mungkin membantu:

    private static Dictionary<Type, Dictionary<string, object>> dicEnum = new Dictionary<Type, Dictionary<string, object>>();
    public static T ToEnum<T>(this string value, T defaultValue)
    {
        var t = typeof(T);
        Dictionary<string, object> dic;
        if (!dicEnum.ContainsKey(t))
        {
            dic = new Dictionary<string, object>();
            dicEnum.Add(t, dic);
            foreach (var en in Enum.GetValues(t))
                dic.Add(en.ToString(), en);
        }
        else
            dic = dicEnum[t];
        if (!dic.ContainsKey(value))
            return defaultValue;
        else
            return (T)dic[value];
    }

1

Saya menemukan bahwa di sini kasus dengan nilai enum yang memiliki nilai EnumMember tidak dipertimbangkan. Jadi di sini kita mulai:

using System.Runtime.Serialization;

public static TEnum ToEnum<TEnum>(this string value, TEnum defaultValue) where TEnum : struct
{
    if (string.IsNullOrEmpty(value))
    {
        return defaultValue;
    }

    TEnum result;
    var enumType = typeof(TEnum);
    foreach (var enumName in Enum.GetNames(enumType))
    {
        var fieldInfo = enumType.GetField(enumName);
        var enumMemberAttribute = ((EnumMemberAttribute[]) fieldInfo.GetCustomAttributes(typeof(EnumMemberAttribute), true)).FirstOrDefault();
        if (enumMemberAttribute?.Value == value)
        {
            return Enum.TryParse(enumName, true, out result) ? result : defaultValue;
        }
    }

    return Enum.TryParse(value, true, out result) ? result : defaultValue;
}

Dan contoh dari enum itu:

public enum OracleInstanceStatus
{
    Unknown = -1,
    Started = 1,
    Mounted = 2,
    Open = 3,
    [EnumMember(Value = "OPEN MIGRATE")]
    OpenMigrate = 4
}

1

Anda harus menggunakan Enum.Parse untuk mendapatkan nilai objek dari Enum, setelah itu Anda harus mengubah nilai objek ke nilai enum tertentu. Melakukan casting ke nilai enum dapat dilakukan dengan menggunakan Convert.ChangeType. Silakan lihat potongan kode berikut

public T ConvertStringValueToEnum<T>(string valueToParse){
    return Convert.ChangeType(Enum.Parse(typeof(T), valueToParse, true), typeof(T));
}

1

Coba contoh ini:

 public static T GetEnum<T>(string model)
    {
        var newModel = GetStringForEnum(model);

        if (!Enum.IsDefined(typeof(T), newModel))
        {
            return (T)Enum.Parse(typeof(T), "None", true);
        }

        return (T)Enum.Parse(typeof(T), newModel.Result, true);
    }

    private static Task<string> GetStringForEnum(string model)
    {
        return Task.Run(() =>
        {
            Regex rgx = new Regex("[^a-zA-Z0-9 -]");
            var nonAlphanumericData = rgx.Matches(model);
            if (nonAlphanumericData.Count < 1)
            {
                return model;
            }
            foreach (var item in nonAlphanumericData)
            {
                model = model.Replace((string)item, "");
            }
            return model;
        });
    }

Dalam sampel ini Anda dapat mengirim setiap string, dan mengatur Enum. Jika Anda Enummemiliki data yang Anda inginkan, kembalikan itu sebagai Enumtipe Anda .


1
Anda menimpa newModelpada setiap baris, jadi jika itu berisi tanda hubung, itu tidak akan diganti. Selain itu, Anda tidak perlu memeriksa apakah string tersebut mengandung sesuatu, Anda dapat memanggil Replacesaja:var newModel = model.Replace("-", "").Replace(" ", "");
Lars Kristensen

@LarsKristensen Ya kita bisa membuat metode yang menghapus karakter nonalphanumeric.
AmirReza-Farahlagha

1

Tidak yakin kapan ini ditambahkan tetapi pada kelas Enum sekarang ada a

Parse<TEnum>(stringValue)

Digunakan seperti itu dengan contoh dalam pertanyaan:

var MyStatus = Enum.Parse<StatusEnum >("Active")

atau mengabaikan casing dengan:

var MyStatus = Enum.Parse<StatusEnum >("active", true)

Berikut adalah metode terurai yang digunakan:

    [NullableContext(0)]
    public static TEnum Parse<TEnum>([Nullable(1)] string value) where TEnum : struct
    {
      return Enum.Parse<TEnum>(value, false);
    }

    [NullableContext(0)]
    public static TEnum Parse<TEnum>([Nullable(1)] string value, bool ignoreCase) where TEnum : struct
    {
      TEnum result;
      Enum.TryParse<TEnum>(value, ignoreCase, true, out result);
      return result;
    }

0
        <Extension()>
    Public Function ToEnum(Of TEnum)(ByVal value As String, ByVal defaultValue As TEnum) As TEnum
        If String.IsNullOrEmpty(value) Then
            Return defaultValue
        End If

        Return [Enum].Parse(GetType(TEnum), value, True)
    End Function

0
public TEnum ToEnum<TEnum>(this string value, TEnum defaultValue){
if (string.IsNullOrEmpty(value))
    return defaultValue;

return Enum.Parse(typeof(TEnum), value, true);}

0

Jika nama properti berbeda dari apa yang Anda inginkan (mis. Perbedaan bahasa), Anda dapat melakukannya seperti ini:

MyType.cs

using System;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

[JsonConverter(typeof(StringEnumConverter))]
public enum MyType
{
    [EnumMember(Value = "person")]
    Person,
    [EnumMember(Value = "annan_deltagare")]
    OtherPerson,
    [EnumMember(Value = "regel")]
    Rule,
}

EnumExtensions.cs

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

public static class EnumExtensions
{
    public static TEnum ToEnum<TEnum>(this string value) where TEnum : Enum
    {
        var jsonString = $"'{value.ToLower()}'";
        return JsonConvert.DeserializeObject<TEnum>(jsonString, new StringEnumConverter());
    }

    public static bool EqualsTo<TEnum>(this string strA, TEnum enumB) where TEnum : Enum
    {
        TEnum enumA;
        try
        {
            enumA = strA.ToEnum<TEnum>();
        }
        catch
        {
            return false;
        }
        return enumA.Equals(enumB);
    }
}

Program.cs

public class Program
{
    static public void Main(String[] args) 
    { 
        var myString = "annan_deltagare";
        var myType = myString.ToEnum<MyType>();
        var isEqual = myString.EqualsTo(MyType.OtherPerson);
        //Output: true
    }     
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.