Bir dizgiyi C # içindeki bir numaralamaya dönüştürme


894

Bir dizgeyi C # numaralandırma değerine dönüştürmenin en iyi yolu nedir?

Numaralandırma değerlerini içeren bir HTML seçme etiketi var. Sayfa gönderildiğinde, (bir dize biçiminde olacak) değeri almak ve numaralandırma değerine dönüştürmek istiyorum.

İdeal bir dünyada böyle bir şey yapabilirdim:

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

ancak bu geçerli bir kod değil.

Yanıtlar:


1508

.NET Core ve .NET> 4'te genel bir ayrıştırma yöntemi vardır :

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

Bu, C # 7'nin yeni satır içi outdeğişkenlerini de içerir , bu nedenle bu, ayrıştırma, açık numara türüne dönüştürme ve myStatusdeğişkeni başlatır + doldurur .

C # 7'ye ve en son .NET'e erişiminiz varsa, bu en iyi yoldur.

Orijinal Yanıt

.NET'te oldukça çirkin (4 veya daha yukarı):

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

Ben ile basitleştirmek eğilimindedir:

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

Sonra yapabilirim:

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

Yorumlarda önerilen seçeneklerden biri, yeterince basit bir uzantı eklemektir:

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

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

Son olarak, dize ayrıştırılamazsa kullanılacak varsayılan bir numaralandırma kullanmak isteyebilirsiniz:

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

Bu çağrı yapar:

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

Ancak, böyle bir uzatma yöntemi string(ad alanı kontrolü olmadan) stringbir enum tutup tutmayacakları gibi görünür (bu yüzden 1234.ToString().ToEnum(StatusEnum.None)geçerli ama saçma olurdu ) olarak eklemek dikkatli olacaktır . Microsoft'un çekirdek sınıflarını, tüm geliştirme ekibiniz bu uzantıların ne yaptığına dair çok iyi bir anlayışa sahip olmadıkça, yalnızca belirli bağlamlarda geçerli olan ek yöntemlerle karıştırmaktan kaçınmak en iyisidir.


17
Performans önemli ise (ki her zaman budur) aşağıda Mckenzieg1 tarafından verilen chk cevap: stackoverflow.com/questions/16100/…
Nash

28
@avinashr, @ McKenzieG1'in cevabı hakkında haklı ama DAİMA önemli değil. Örneğin, her ayrıştırma için bir DB çağrısı yapıyorsanız, numaralandırma ayrıştırma konusunda endişelenmek anlamsız bir mikro optimizasyon olacaktır.
Keith

4
@HM Burada bir uzantının uygun olduğunu düşünmüyorum - bu biraz özel bir durumdur ve her dize için bir uzantı geçerli olur . Eğer gerçekten yapmak isteseydiniz bu önemsiz bir değişiklik olurdu.
Keith

7
Enum.TryParse'a ne dersin?
Elaine

15
çok hoş. son örneğinizde bir T: yapısına ihtiyacınız var.
bbrik

330

Kullanım Enum.TryParse<T>(String, T)(≥ .NET 4.0):

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

C # 7.0'ın parametre tipi satır içi ayarı ile daha da basitleştirilebilir :

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

45
Büyük / küçük harf duyarlılığı için orta boolean parametresini ekleyin ve bu şimdiye kadarki en güvenli ve en zarif çözümdür.
DanM7

18
Hadi, kaç kişinin 2008'den bu seçili cevabı sadece aşağı kaydırıp bulmak için uyguladığınız daha iyi (modern) cevap.
TEK

@TEK Aslında 2008 cevabını tercih ediyorum.
Sıfır3

Anlamıyorum. Parsedönüşümle ilgili yanlış giden şeyler için açıklayıcı istisnalar atar (değer nullboştu veya karşılık gelen numaralandırma sabiti yok), bu da TryParseboolean dönüş değerinden (somut hatayı bastırır) çok daha iyi
yair

2
Tamsayı dizeleri ayrıştırılırken Enum.TryParse <T> (String, T) hatalı. Örneğin, bu kod, saçma bir dizgeyi saçma bir numaralandırma olarak başarıyla ayrıştırır: var result = Enum.TryParse<System.DayOfWeek>("55", out var parsedEnum);
Mass Dot Net

196

Performansının Enum.Parse()korkunç olduğunu unutmayın , çünkü yansıma yoluyla uygulanır. (Aynısı geçerlidir Enum.ToString, bu da diğer yöne gider.)

Performansa duyarlı kodda dizeleri Enum'a dönüştürmeniz gerekiyorsa, en iyi bahis Dictionary<String,YourEnum>başlangıçta bir oluşturmak ve bunu dönüşümlerinizi yapmak için kullanmaktır.


10
Bir masaüstünde, ilk çalıştırmada bir Enum'a bir dize dönüştürmek için 3ms ölçtüm. (Sadece beceriksizlik seviyesini göstermek için).
Matthieu Charbonnier

12
Vay 3ms korkunç büyüklüğü emir
John Stock

1
Bunun etrafına bir kod örneği ekleyebilir misiniz, bu yüzden nasıl değiştirileceği ve kullanılacağı hakkında bir fikir edinebiliriz
transformatör

Uygulamanız 1 milyon kişi tarafından kullanılıyorsa => tükettiğiniz insan ömrünü 50 saate kadar ekler :) Tek bir sayfa kullanımında. : P
Cătălin Rădoi


31

Artık uzantı yöntemlerini kullanabilirsiniz :

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

Ve onları aşağıdaki kodla çağırabilirsiniz (burada, FilterTypebir enum türüdür):

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

1
Bu değeri nesne olarak almak ve bu yöntemin içinde dizeye döküm için güncelleştirdim. Bu şekilde bir int değeri sadece dizeleri yerine .ToEnum alabilirim.
RealSollyM

2
@SollyM Bunun korkunç bir fikir olduğunu söyleyebilirim, çünkü bu uzantı yöntemi tüm nesne türleri için geçerli olacaktır . Biri dize ve biri int için olmak üzere iki uzatma yöntemi, bence daha temiz ve çok daha güvenli olurdu.
Svish

@Svish, bu doğru. Bunu yapmamın tek nedeni, kodumuzun yalnızca dahili olarak kullanılması ve 2 uzantı yazmaktan kaçınmak istedim. Ve Enum'a dönüştürdüğümüz tek zaman dize veya int ile olduğu için, bunun başka bir sorun olduğunu görmedim.
RealSollyM

3
@SollyM Dahili ya da değil, hala kodumu koruyan ve kullanan kişiyim: Her intellisense menüsünde bir ToEnum bulduğumda PI rahatsız olacak ve dediğiniz gibi, bir numaralandırmaya dönüştüğünüz tek zaman dizedir veya int, yalnızca bu iki yönteme ihtiyaç duyacağınızdan emin olabilirsiniz. Ve iki yöntem birden fazla değil, özellikle de bu kadar küçük ve yardımcı program türlerinde: P
Svish

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

Eğer ruh hali adlı bir numaralandırmanız olsaydı şöyle olurdu:

   enum Mood
   {
      Angry,
      Happy,
      Sad
   } 

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

18

DİKKAT:

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

Enum.(Try)Parse() virgülle ayrılmış birden çok bağımsız değişkeni kabul eder ve bunları ikili 'veya' ile birleştirir| . Bunu devre dışı bırakamazsınız ve bence neredeyse hiç istemezsiniz.

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

ThreeTanımlanmamış olsa bile , xyine de int değeri alır 3. Daha da kötüsü: Enum.Parse () size numaralandırma için tanımlanmamış bir değer verebilir!

Kullanıcıların isteyerek veya istemeyerek bu davranışı tetikleyen sonuçlarını yaşamak istemem.

Ek olarak, başkaları tarafından belirtildiği gibi, performans büyük numaralar için ideal olandan daha azdır, yani olası değerlerin sayısında doğrusaldır.

Aşağıdakileri öneririm:

    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
Aslında bunu bilmek çok faydalıdır Enum.(Try)Parse accepts multiple, comma-separated arguments, and combines them with binary 'or'. Enum değerlerinizi 2'nin gücü olarak ayarlayabileceğiniz ve birden fazla boole bayrakını ayrıştırmanın çok kolay bir yoluna sahip olduğunuz anlamına gelir. "UseSSL, NoRetries, Sync". Aslında muhtemelen bunun için tasarlandı.
pcdev

16

Enum.Parse senin arkadaşın:

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

13

İstisnaları önlemek için kabul edilen yanıtı varsayılan bir değerle genişletebilirsiniz:

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

Sonra şöyle diyorsunuz:

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

Varsayılan değer bir enum değilse, Enum.TryParse başarısız olur ve yakalanan bir istisna atar.

Kodumuzda birçok yerde bu işlevi yıllarca kullandıktan sonra, bu işlemin performansa mal olduğu bilgisini eklemek iyi olabilir!


Varsayılan değerleri sevmiyorum. Öngörülemeyen sonuçlara yol açabilir.
Daniël Tulp

5
bu ne zaman istisna atacak?
andleer

enum değeri varsayılan değerle aynı enum türüne uymuyorsa @andleer
Nelly

@Nelly Eski kod burada ama defaultValueve yöntem dönüş türü hem tür T. Türler farklıysa, bir derleme zamanı hatası alırsınız: "'ConsoleApp1.Size' den 'ConsoleApp1.Color' 'a dönüştürülemiyor" veya türleriniz ne olursa olsun.
andleer

@andleer, üzgünüm sana son cevabım doğru değildi. Birinin bu işlevi enum türünde olmayan bir varsayılan değerle çağırması durumunda bu yöntemin bir Syste.ArgumentException durumu oluşturması mümkündür. C # 7.0 ile T: Enum'un nerede cümlesi yapamadım. Bu yüzden bu olasılığı bir deneme yakalama ile yakaladım.
Nelly

8

Mükemmel bir şekilde geçerli girdi kabul edemedik ve @ Keith'in cevabının bu varyasyonuyla devam ettik:

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

Dize, try / catch ve .NET 4.5 TryParse () yöntemi olmadan TEnum ayrıştırır

/// <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
Kod zaten bir açıklama içeriyorsa açıklama yapmak gerekip gerekmediği? Tamam, bunu yaptım :)
jite.gs

3

TryParse kullanarak süper basit kod:

var value = "Active";

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

2

Uzantı yöntemi çözümünü seviyorum.

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

Aşağıda testler ile uygulamam.

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

==================== Eksiksiz Bir Program ====================

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

Performans için bu yardımcı olabilir:

    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

Burada EnumMember değeri olan enum değerleri olan vakanın dikkate alınmadığını tespit ettim. İşte başlıyoruz:

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

Ve bu numaralandırma örneği:

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

1

Nesne değerini Enum'dan almak için Enum.Parse öğesini kullanmanız gerekir; bundan sonra nesne değerini belirli numaralandırma değerine değiştirmeniz gerekir. Enum değerine döküm Convert.ChangeType kullanılarak yapılabilir. Lütfen aşağıdaki kod snippet'ine bir göz atın

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

1

Bu örneği deneyin:

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

Bu örnekte her dizeyi gönderebilir ve Enum. İstediğiniz Enumverileriniz varsa, bunu Enumtürünüz olarak döndürün.


1
newModelHer satırın üzerine yazıyorsunuz, bu nedenle tire içeriyorsa değiştirilmeyecektir. Ayrıca, ipin bir şey içerip içermediğini kontrol etmek zorunda değilsiniz, Replaceyine de arayabilirsiniz :var newModel = model.Replace("-", "").Replace(" ", "");
Lars Kristensen

@LarsKristensen Evet, alfasayısal olmayan karakteri kaldıran bir yöntem oluşturabiliriz.
AmirReza-Farahlagha

1

Bunun ne zaman eklendiğinden emin değilim ama Enum sınıfında artık bir

Parse<TEnum>(stringValue)

Söz konusu örnekte böyle kullanılır:

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

veya muhafazayı göz ardı ederek:

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

İşte bunun kullandığı ayrıştırılmış yöntemler:

    [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

Özellik adı, adlandırmak istediğiniz addan farklıysa (örneğin, dil farklılıkları) aşağıdakileri yapabilirsiniz:

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