Enum değeri için TryParse nasıl kullanılır?


97

Belirli bir değeri (bir dize olarak geçirilen) bir enum. Bir eşleşme durumunda, enum örneğini döndürmelidir; aksi takdirde, varsayılan bir değer döndürmelidir.

İşlev, geçersiz bir argüman verildiğinde bir istisna oluşturan , kullanımını hariç tutan try/ kullanamaz .catchEnum.Parse

Bunu TryParseuygulamak için bir fonksiyonun çizgileri boyunca bir şey kullanmak istiyorum :

public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
   object enumValue;
   if (!TryParse (typeof (TEnum), strEnumValue, out enumValue))
   {
       return defaultValue;
   }
   return (TEnum) enumValue;
}

8
Bu soruyu anlamıyorum; "Bu sorunu çözmek istiyorum, ancak bana bir çözüm verecek yöntemlerden herhangi birini kullanmak istemiyorum" diyorsunuz. Amaç ne?
Domenic

1
Çözümü denemek / yakalamaktan kaçınma eğiliminiz nedir? 'Pahalı' oldukları için İstisna durumundan kaçınmaya çalışıyorsanız, lütfen kendinize biraz ara verin. Vakaların% 99'unda, maliyet istisnası fırlatma / yakalama maliyeti, ana kodunuza kıyasla önemsizdir.
SolutionYogi

4
@Domenic: Bildiğimden daha iyi bir çözüm arıyorum. Zaten bildiğiniz bir rota veya tren istemek için bir demiryolu araştırmasına gider miydiniz :).
Manish Basantani

2
@Yogi, @Thorarin: try ... catch her zaman son seçimim olurdu. Maliyetli olmakla ilgili asla bilemeyiz. Ya birisi yarar yöntemimi 100'lerce öğeden oluşan bir liste üzerinden çağırırsa?
Manish Basantani

2
@Amby, sadece bir dene / yakala bloğuna girmenin maliyeti ihmal edilebilir. Bir istisna ATMANIN maliyeti değil, ama o zaman bunun istisnai olması gerekiyor, değil mi? Ayrıca, "asla bilmiyoruz" demeyin ... kodun profilini çıkarın ve öğrenin. Bir şeyin yavaş olup olmadığını merak ederek zamanınızı boşa harcamayın, BULUN!
akmad

Yanıtlar:


31

Başkalarının dediği gibi, kendinizinkini uygulamak zorundasınız TryParse. Simon Mourier, her şeyle ilgilenen eksiksiz bir uygulama sağlıyor.

Bit alanı numaralandırmaları (yani bayraklar) kullanıyorsanız, "MyEnum.Val1|MyEnum.Val2"iki enum değerinin birleşimine benzer bir dizeyi de işlemeniz gerekir . Eğer sadece Enum.IsDefinedbu dizge ile çağırırsanız , Enum.Parsedoğru bir şekilde işlese bile yanlış döndürür .

Güncelleme

Lisa ve Christian tarafından yorumlarda belirtildiği gibi Enum.TryParse, artık .NET4 ve sonraki sürümlerde C # için kullanılabilir.

MSDN Belgeleri


Belki de en az seksi, ancak kodunuz .NET 4'e taşınana kadar bunun kesinlikle en iyisi olduğuna katılıyorum.
Lisa

1
Aşağıda belirtildiği gibi, ancak gerçekten görünmez: .Net 4 itibariyle, TryParse kullanılabilir ve ekstra kodlama olmadan çalışır. MSDN'den daha fazla bilgi edinilebilir: msdn.microsoft.com/library/vstudio/dd991317%28v=vs.100%29.aspx
Christian

106

Enum.IsDefined işleri halletecek. Bir TryParse'ın muhtemelen olabileceği kadar verimli olmayabilir, ancak istisna işleme olmadan çalışacaktır.

public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
    if (!Enum.IsDefined(typeof(TEnum), strEnumValue))
        return defaultValue;

    return (TEnum)Enum.Parse(typeof(TEnum), strEnumValue);
}

Dikkat çekmeye değer: TryParse.NET 4.0'a bir yöntem eklendi.


1
Şimdiye kadar gördüğüm en iyi cevap ... deneme / yakalama yok, GetNames yok :)
Thomas Levesque

13

6
Ayrıca hiçbir Tanımlımı davayı orada görmezden edilir
Anthony Johnston

2
@Anthony: Eğer büyük / küçük harf duyarlılığını desteklemek istiyorsanız, ihtiyacınız olacak GetNames. Dahili olarak, tüm bu yöntemler (dahil Parse) GetHashEntry, gerçek yansımayı yapan - bir kez kullanır. İşin iyi tarafı, .NET 4.0'da bir TryParse var ve o da geneldir :)
Thorarin

+1 Günümü kurtardı! NET 4'ten .NET 3.5'e bir grup kodu geri
yüklüyorum

20

İşte özel bir uygulaması EnumTryParse. Diğer yaygın uygulamaların aksine, Flagsöznitelikle işaretlenmiş numaralandırmayı da destekler .

    /// <summary>
    /// Converts the string representation of an enum to its Enum equivalent value. A return value indicates whether the operation succeeded.
    /// This method does not rely on Enum.Parse and therefore will never raise any first or second chance exception.
    /// </summary>
    /// <param name="type">The enum target type. May not be null.</param>
    /// <param name="input">The input text. May be null.</param>
    /// <param name="value">When this method returns, contains Enum equivalent value to the enum contained in input, if the conversion succeeded.</param>
    /// <returns>
    /// true if s was converted successfully; otherwise, false.
    /// </returns>
    public static bool EnumTryParse(Type type, string input, out object value)
    {
        if (type == null)
            throw new ArgumentNullException("type");

        if (!type.IsEnum)
            throw new ArgumentException(null, "type");

        if (input == null)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        input = input.Trim();
        if (input.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        string[] names = Enum.GetNames(type);
        if (names.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        Type underlyingType = Enum.GetUnderlyingType(type);
        Array values = Enum.GetValues(type);
        // some enums like System.CodeDom.MemberAttributes *are* flags but are not declared with Flags...
        if ((!type.IsDefined(typeof(FlagsAttribute), true)) && (input.IndexOfAny(_enumSeperators) < 0))
            return EnumToObject(type, underlyingType, names, values, input, out value);

        // multi value enum
        string[] tokens = input.Split(_enumSeperators, StringSplitOptions.RemoveEmptyEntries);
        if (tokens.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        ulong ul = 0;
        foreach (string tok in tokens)
        {
            string token = tok.Trim(); // NOTE: we don't consider empty tokens as errors
            if (token.Length == 0)
                continue;

            object tokenValue;
            if (!EnumToObject(type, underlyingType, names, values, token, out tokenValue))
            {
                value = Activator.CreateInstance(type);
                return false;
            }

            ulong tokenUl;
            switch (Convert.GetTypeCode(tokenValue))
            {
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.SByte:
                    tokenUl = (ulong)Convert.ToInt64(tokenValue, CultureInfo.InvariantCulture);
                    break;

                //case TypeCode.Byte:
                //case TypeCode.UInt16:
                //case TypeCode.UInt32:
                //case TypeCode.UInt64:
                default:
                    tokenUl = Convert.ToUInt64(tokenValue, CultureInfo.InvariantCulture);
                    break;
            }

            ul |= tokenUl;
        }
        value = Enum.ToObject(type, ul);
        return true;
    }

    private static char[] _enumSeperators = new char[] { ',', ';', '+', '|', ' ' };

    private static object EnumToObject(Type underlyingType, string input)
    {
        if (underlyingType == typeof(int))
        {
            int s;
            if (int.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(uint))
        {
            uint s;
            if (uint.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(ulong))
        {
            ulong s;
            if (ulong.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(long))
        {
            long s;
            if (long.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(short))
        {
            short s;
            if (short.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(ushort))
        {
            ushort s;
            if (ushort.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(byte))
        {
            byte s;
            if (byte.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(sbyte))
        {
            sbyte s;
            if (sbyte.TryParse(input, out s))
                return s;
        }

        return null;
    }

    private static bool EnumToObject(Type type, Type underlyingType, string[] names, Array values, string input, out object value)
    {
        for (int i = 0; i < names.Length; i++)
        {
            if (string.Compare(names[i], input, StringComparison.OrdinalIgnoreCase) == 0)
            {
                value = values.GetValue(i);
                return true;
            }
        }

        if ((char.IsDigit(input[0]) || (input[0] == '-')) || (input[0] == '+'))
        {
            object obj = EnumToObject(underlyingType, input);
            if (obj == null)
            {
                value = Activator.CreateInstance(type);
                return false;
            }
            value = obj;
            return true;
        }

        value = Activator.CreateInstance(type);
        return false;
    }

1
en iyi uygulamayı sağladınız ve ben bunu kendi amaçlarım için kullandım; ancak, neden Activator.CreateInstance(type)varsayılan enum değerini oluşturmak için kullandığınızı ve oluşturmadığınızı merak ediyorum Enum.ToObject(type, 0). Sadece bir zevk meselesi mi?
Pierre Arnaud

1
@Pierre - Hmmm ... hayır, o zaman daha doğal görünüyordu :-) Belki Enum.ToObject dahili bir dahili arama InternalBoxEnum kullandığından daha hızlıdır? Bunu hiç kontrol etmedim ...
Simon Mourier

2
Aşağıda belirtildiği gibi, ancak gerçekten görünmez: .Net 4 itibariyle, TryParse kullanılabilir ve ekstra kodlama olmadan çalışır. MSDN'den daha fazla bilgi edinilebilir: msdn.microsoft.com/library/vstudio/dd991317%28v=vs.100%29.aspx
Christian

16

Sonunda, bunu etrafına uygulamanız gerekir Enum.GetNames:

public bool TryParseEnum<T>(string str, bool caseSensitive, out T value) where T : struct {
    // Can't make this a type constraint...
    if (!typeof(T).IsEnum) {
        throw new ArgumentException("Type parameter must be an enum");
    }
    var names = Enum.GetNames(typeof(T));
    value = (Enum.GetValues(typeof(T)) as T[])[0];  // For want of a better default
    foreach (var name in names) {
        if (String.Equals(name, str, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)) {
            value = (T)Enum.Parse(typeof(T), name);
            return true;
        }
    }
    return false;
}

Ek Notlar:

  • Enum.TryParse.NET 4'e dahildir. Buraya bakın http://msdn.microsoft.com/library/dd991876(VS.100).aspx
  • Başka bir yaklaşım, Enum.Parsebaşarısız olduğunda atılan istisnayı yakalayarak doğrudan sarmalamak olacaktır . Bu, bir eşleşme bulunduğunda daha hızlı olabilir, ancak yoksa muhtemelen daha yavaş olacaktır. İşlediğiniz verilere bağlı olarak, bu net bir gelişme olabilir veya olmayabilir.

DÜZENLEME: Bunun hakkında gerekli bilgileri önbelleğe alan daha iyi bir uygulama gördüm: http://damieng.com/blog/2010/10/17/enums-better-syntax-improved-performance-and-tryparse-in-net- 3-5


Varsayılan değeri ayarlamak için varsayılan (T) kullanmanızı önerecektim. Bunun tüm numaralandırmalar için işe yaramayacağı ortaya çıktı. Örneğin, enum için temel alınan tür int default (T) ise, her zaman 0 döndürür ve bu, enum için geçerli olabilir veya olmayabilir.
Daniel Ballinger

Damieng en blog uygulaması yok değil ile Çeteleler desteklemek Flagsözniteliği.
Uwe Keim

9

.NET 4.5'e göre

Aşağıdaki örnek kod

using System;

enum Importance
{
    None,
    Low,
    Medium,
    Critical
}

class Program
{
    static void Main()
    {
    // The input value.
    string value = "Medium";

    // An unitialized variable.
    Importance importance;

    // Call Enum.TryParse method.
    if (Enum.TryParse(value, out importance))
    {
        // We now have an enum type.
        Console.WriteLine(importance == Importance.Medium);
    }
    }
}

Referans: http://www.dotnetperls.com/enum-parse


4

UnconstrainedMelody'de kullanabileceğiniz optimize edilmiş bir uygulamaya sahibim . Etkili bir şekilde sadece isim listesini önbelleğe alıyor, ancak bunu güzel, güçlü bir şekilde yazılmış, genel olarak kısıtlı bir şekilde yapıyor :)


4
enum EnumStatus
{
    NAO_INFORMADO = 0,
    ENCONTRADO = 1,
    BLOQUEADA_PELO_ENTREGADOR = 2,
    DISPOSITIVO_DESABILITADO = 3,
    ERRO_INTERNO = 4,
    AGARDANDO = 5
}

...

if (Enum.TryParse<EnumStatus>(item.status, out status)) {

}

2

Şu anda kutunun dışında Enum. TryParse yok. Bu, Connect'te talep edildi ( Hala Enum. TryParse yok ) ve .NET 3.5'ten sonra bir sonraki çerçeveye olası dahil edildiğini belirten bir yanıt aldı. Şimdilik önerilen geçici çözümleri uygulamanız gerekecek.


1

İstisna işlemeyi önlemenin tek yolu GetNames () yöntemini kullanmaktır ve hepimiz istisnaların genel uygulama mantığı için kötüye kullanılmaması gerektiğini biliyoruz :)


1
Tek yol bu değil . Enum.IsDefined (..), kullanıcı kodunda istisnaların atılmasını önleyecektir.
Thorarin

1

Dinamik olarak oluşturulmuş bir işlevi / sözlüğü önbelleğe almaya izin verilebilir mi?

Önceden numaralandırmanın türünü bilmediğiniz (göründüğü gibi) için, ilk yürütme, sonraki yürütmelerin yararlanabileceği bir şey oluşturabilir.

Enum.GetNames () sonucunu bile önbelleğe alabilirsiniz.

CPU veya Bellek için optimize etmeye mi çalışıyorsunuz? Eğer Do gerçekten gerek?


Fikir, CPU'yu optimize etmektir. Bunu maliyet hafızasında yapabileceğime katılıyorum. Ama aradığım çözüm bu değil. Teşekkürler.
Manish Basantani

0

Diğerlerinin daha önce de söylediği gibi, Try & Catch kullanmıyorsanız, IsDefined veya GetNames kullanmanız gerekir ... İşte bazı örnekler ... temelde hepsi aynıdır, ilki null yapılabilir numaralandırmaları işliyor. 2. olanı numaralandırma değil dizelerin uzantısı olduğu için tercih ederim ... ama onları istediğiniz gibi karıştırabilirsiniz!

  • www.objectreference.net/post/Enum- TryParse-Extension-Method.aspx
  • flatlinerdoa.spaces.live.com/blog/cns!17124D03A9A052B0!605.entry
  • mironabramson.com/blog/post/2008/03/Another-version-for-the-missing-method-Enum TryParse.aspx
  • lazyloading.blogspot.com/2008/04/enumtryparse-with-net-35-extension.html

0

Bir TryParse yoktur çünkü Enum'un türü çalışma zamanına kadar bilinmemektedir. Date. TryParse yöntemiyle aynı metodolojiyi izleyen bir TryParse, ByRef parametresinde örtük bir dönüştürme hatası atar.

Bunun gibi bir şey yapmanızı öneririm:

//1 line call to get value
MyEnums enumValue = (Sections)EnumValue(typeof(Sections), myEnumTextValue, MyEnums.SomeEnumDefault);

//Put this somewhere where you can reuse
public static object EnumValue(System.Type enumType, string value, object NotDefinedReplacement)
{
    if (Enum.IsDefined(enumType, value)) {
        return Enum.Parse(enumType, value);
    } else {
        return Enum.Parse(enumType, NotDefinedReplacement);
    }
}

İçin Tryolan sonuçlar yöntemler değer türleri olabilir veya burada null, meşru bir sonucu olabilir (örneğin Dictionary.TryGetValue, which has both such traits), the normal pattern is for a Try` yöntem dönmek için boolve bir şekilde geçmesi sonucunu outparametresi. Olanlar için geri sınıfı türleri burada nullgeçerli bir sonuç değil, bir kullanan herhangi bir zorluk vardır nulldönüş başarısızlığı belirtmek için.
supercat

-1

Enum sınıfının (struct?) Kendisine bir göz atın. Bunun üzerinde bir Ayrıştırma yöntemi var ama bir triparse konusunda emin değilim.


Enum.Parse (typeof (TEnum), strEnumValue) yöntemini biliyorum. StrEnumValue geçerli değilse ArgumentException oluşturur. TryParse
aranıyor

-2

Bu yöntem bir tür numaralandırmayı dönüştürecek:

  public static TEnum ToEnum<TEnum>(object EnumValue, TEnum defaultValue)
    {
        if (!Enum.IsDefined(typeof(TEnum), EnumValue))
        {
            Type enumType = Enum.GetUnderlyingType(typeof(TEnum));
            if ( EnumValue.GetType() == enumType )
            {
                string name = Enum.GetName(typeof(HLink.ViewModels.ClaimHeaderViewModel.ClaimStatus), EnumValue);
                if( name != null)
                    return (TEnum)Enum.Parse(typeof(TEnum), name);
                return defaultValue;
            }
        }
        return (TEnum)Enum.Parse(typeof(TEnum), EnumValue.ToString());
    } 

Temeldeki türü kontrol eder ve ayrıştırmak için adı karşısına alır. Her şey başarısız olursa, varsayılan değeri döndürür.


3
bu ne yapıyor "Enum.GetName (typeof (HLink.ViewModels.ClaimHeaderViewModel.ClaimStatus), EnumValue)" Muhtemelen yerel kodunuza bazı bağımlılıklar.
Manish Basantani
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.