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.

Ben Mills
sumber

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.

Keith
sumber
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);
Erwin Mayer
sumber
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.

McKenzieG1
sumber
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
95

Anda sedang mencari Enum.Parse .

SomeEnum enum = (SomeEnum)Enum.Parse(typeof(SomeEnum), "EnumValue");
David Whitney
sumber
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>();
Foyzul Karim
sumber
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());
brendan
sumber
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);
    }
Timo
sumber
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");
tags2k
sumber
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!

Nelly
sumber
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;
}
celah
sumber
7
// str.ToEnum<EnumType>()
T static ToEnum<T>(this string str) 
{ 
    return (T) Enum.Parse(typeof(T), str);
}
Mark Cidade
sumber
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;
}
jite.gs
sumber
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;
Brian Rice
sumber
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
        }
alhpe
sumber
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.
Rae Lee
sumber
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

Patrik Lindström
sumber
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];
    }
Koray
sumber
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
}
pembuat isx
sumber
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));
}
Bartosz Gawron
sumber
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 .

AmirReza-Farahlagha
sumber
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;
    }
JCisar
sumber
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
AHMED RABEE
sumber
0
public TEnum ToEnum<TEnum>(this string value, TEnum defaultValue){
if (string.IsNullOrEmpty(value))
    return defaultValue;

return Enum.Parse(typeof(TEnum), value, true);}
AHMED RABEE
sumber
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
    }     
}
Joel Wiklund
sumber