İnt C # yasal numaralandırma olup olmadığını kontrol etmenin bir yolu var mı?


168

Birkaç SO mesajları okudum ve en temel işlem eksik görünüyor.

public enum LoggingLevel
{
    Off = 0,
    Error = 1,
    Warning = 2,
    Info = 3,
    Debug = 4,
    Trace = 5
};

if (s == "LogLevel")
{
    _log.LogLevel = (LoggingLevel)Convert.ToInt32("78");
    _log.LogLevel = (LoggingLevel)Enum.Parse(typeof(LoggingLevel), "78");
    _log.WriteDebug(_log.LogLevel.ToString());
}

Bu istisnalara neden olmaz, saklamaktan mutluluk duyar 78. Bir numaralandırmaya giden bir değeri doğrulamanın bir yolu var mı?


Yanıtlar:


272

Numaralandır'a göz atın

Kullanımı:

if(Enum.IsDefined(typeof(MyEnum), value))
    MyEnum a = (MyEnum)value; 

Bu sayfadaki örnek:

using System;    
[Flags] public enum PetType
{
   None = 0, Dog = 1, Cat = 2, Rodent = 4, Bird = 8, Reptile = 16, Other = 32
};

public class Example
{
   public static void Main()
   {
      object value;     
      // Call IsDefined with underlying integral value of member.
      value = 1;
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      // Call IsDefined with invalid underlying integral value.
      value = 64;
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      // Call IsDefined with string containing member name.
      value = "Rodent";
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      // Call IsDefined with a variable of type PetType.
      value = PetType.Dog;
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      value = PetType.Dog | PetType.Cat;
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      // Call IsDefined with uppercase member name.      
      value = "None";
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      value = "NONE";
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      // Call IsDefined with combined value
      value = PetType.Dog | PetType.Bird;
      Console.WriteLine("{0:D}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      value = value.ToString();
      Console.WriteLine("{0:D}: {1}", value, Enum.IsDefined(typeof(PetType), value));
   }
}

Örnekte aşağıdaki çıktı gösterilmektedir:

//       1: True
//       64: False
//       Rodent: True
//       Dog: True
//       Dog, Cat: False
//       None: True
//       NONE: False
//       9: False
//       Dog, Bird: False

@matti: "78" değerini LoggingLevel, depolama alanı olarak kullanılan sayıya dönüştürün , ardından bunu bir LoggingLevelenum değeri olarak gösterin .
thecoop

9
IsDefinedBitwised numaralandırma üyeleri için çalışmıyor gibi görünüyor .
Saeed Neamati

29

Yukarıdaki çözümler [Flags]durumlarla ilgilenmez .

Aşağıdaki çözümüm bazı performans sorunlarına sahip olabilir (eminim ki çeşitli şekillerde optimizasyon yapabilirim) ama aslında her zaman bir enum değerinin geçerli olup olmadığını kanıtlayacaktır .

Üç varsayım üzerine kuruludur:

  • C # 'daki enum değerlerine sadece izin verilir int, kesinlikle başka bir şey
  • C # enum adları gerekir alfabetik bir karakterle başlamalıdır
  • Eksi işaretiyle geçerli bir numaralandırma adı olamaz: -

Numaralandırma ToString()çağrıldığında, intnumaralandırma (bayrak ya da bayrak) eşleşmediğinde değer döndürülür . İzin verilen bir numaralandırma değeri eşleşirse, eşleşmelerin adını yazdırır.

Yani:

[Flags]
enum WithFlags
{
    First = 1,
    Second = 2,
    Third = 4,
    Fourth = 8
}

((WithFlags)2).ToString() ==> "Second"
((WithFlags)(2 + 4)).ToString() ==> "Second, Third"
((WithFlags)20).ToString() ==> "20"

Bu iki kuralı göz önünde bulundurarak, .NET Framework işini doğru yaparsa, geçerli bir numaralandırma ToString()yöntemine yapılan çağrıların ilk karakteri olarak alfabetik bir karaktere sahip bir şeyle sonuçlanacağını varsayabiliriz :

public static bool IsValid<TEnum>(this TEnum enumValue)
    where TEnum : struct
{
    var firstChar = enumValue.ToString()[0];
    return (firstChar < '0' || firstChar > '9') && firstChar != '-';
}

Biri "hack" olarak adlandırılabilir, ancak avantajları Microsoft'un kendi uygulamalarına Enumve C # standartlarına dayanarak, kendi potansiyel olarak buggy kodunuza veya çeklerinize güvenmemenizdir. Performansın son derece kritik olmadığı durumlarda, bu çok fazla kötü switchifade veya diğer kontrolleri kurtaracaktır !

Düzenle

@ChaseMedallion'a, orijinal uygulamamın negatif değerleri desteklemediğine işaret ettiği için teşekkürler. Bu düzeltildi ve testler sağlandı.

Ve bunu destekleyecek testler:

[TestClass]
public class EnumExtensionsTests
{
    [Flags]
    enum WithFlags
    {
        First = 1,
        Second = 2,
        Third = 4,
        Fourth = 8
    }

    enum WithoutFlags
    {
        First = 1,
        Second = 22,
        Third = 55,
        Fourth = 13,
        Fifth = 127
    }

    enum WithoutNumbers
    {
        First, // 1
        Second, // 2
        Third, // 3
        Fourth // 4
    }

    enum WithoutFirstNumberAssigned
    {
        First = 7,
        Second, // 8
        Third, // 9
        Fourth // 10
    }


    enum WithNagativeNumbers
    {
        First = -7,
        Second = -8,
        Third = -9,
        Fourth = -10
    }

    [TestMethod]
    public void IsValidEnumTests()
    {
        Assert.IsTrue(((WithFlags)(1 | 4)).IsValid());
        Assert.IsTrue(((WithFlags)(1 | 4)).IsValid());
        Assert.IsTrue(((WithFlags)(1 | 4 | 2)).IsValid());
        Assert.IsTrue(((WithFlags)(2)).IsValid());
        Assert.IsTrue(((WithFlags)(3)).IsValid());
        Assert.IsTrue(((WithFlags)(1 + 2 + 4 + 8)).IsValid());

        Assert.IsFalse(((WithFlags)(16)).IsValid());
        Assert.IsFalse(((WithFlags)(17)).IsValid());
        Assert.IsFalse(((WithFlags)(18)).IsValid());
        Assert.IsFalse(((WithFlags)(0)).IsValid());

        Assert.IsTrue(((WithoutFlags)1).IsValid());
        Assert.IsTrue(((WithoutFlags)22).IsValid());
        Assert.IsTrue(((WithoutFlags)(53 | 6)).IsValid());   // Will end up being Third
        Assert.IsTrue(((WithoutFlags)(22 | 25 | 99)).IsValid()); // Will end up being Fifth
        Assert.IsTrue(((WithoutFlags)55).IsValid());
        Assert.IsTrue(((WithoutFlags)127).IsValid());

        Assert.IsFalse(((WithoutFlags)48).IsValid());
        Assert.IsFalse(((WithoutFlags)50).IsValid());
        Assert.IsFalse(((WithoutFlags)(1 | 22)).IsValid());
        Assert.IsFalse(((WithoutFlags)(9 | 27 | 4)).IsValid());

        Assert.IsTrue(((WithoutNumbers)0).IsValid());
        Assert.IsTrue(((WithoutNumbers)1).IsValid());
        Assert.IsTrue(((WithoutNumbers)2).IsValid());
        Assert.IsTrue(((WithoutNumbers)3).IsValid());
        Assert.IsTrue(((WithoutNumbers)(1 | 2)).IsValid()); // Will end up being Third
        Assert.IsTrue(((WithoutNumbers)(1 + 2)).IsValid()); // Will end up being Third

        Assert.IsFalse(((WithoutNumbers)4).IsValid());
        Assert.IsFalse(((WithoutNumbers)5).IsValid());
        Assert.IsFalse(((WithoutNumbers)25).IsValid());
        Assert.IsFalse(((WithoutNumbers)(1 + 2 + 3)).IsValid());

        Assert.IsTrue(((WithoutFirstNumberAssigned)7).IsValid());
        Assert.IsTrue(((WithoutFirstNumberAssigned)8).IsValid());
        Assert.IsTrue(((WithoutFirstNumberAssigned)9).IsValid());
        Assert.IsTrue(((WithoutFirstNumberAssigned)10).IsValid());

        Assert.IsFalse(((WithoutFirstNumberAssigned)11).IsValid());
        Assert.IsFalse(((WithoutFirstNumberAssigned)6).IsValid());
        Assert.IsFalse(((WithoutFirstNumberAssigned)(7 | 9)).IsValid());
        Assert.IsFalse(((WithoutFirstNumberAssigned)(8 + 10)).IsValid());

        Assert.IsTrue(((WithNagativeNumbers)(-7)).IsValid());
        Assert.IsTrue(((WithNagativeNumbers)(-8)).IsValid());
        Assert.IsTrue(((WithNagativeNumbers)(-9)).IsValid());
        Assert.IsTrue(((WithNagativeNumbers)(-10)).IsValid());
        Assert.IsFalse(((WithNagativeNumbers)(-11)).IsValid());
        Assert.IsFalse(((WithNagativeNumbers)(7)).IsValid());
        Assert.IsFalse(((WithNagativeNumbers)(8)).IsValid());
    }
}

1
Bunun için teşekkürler, geçerli bayrak kombinasyonlarıyla ilgili benzer bir sorunum vardı. Numaralandırmanın ilk karakterini denetlemeye alternatif olarak, int.TryParse (enumValue.ToString ()) öğesine de deneyebilirsiniz ... Başarısız olursa, geçerli bir bayrak kümeniz vardır. Bu aslında çözümünüzden daha yavaş olabilir.
MadHenchbot

Kontrol, rakam olmayan karakterler için olduğundan, bu uygulama negatif değerleri doğru olarak doğrulayamıyor
ChaseMedallion

İyi yakalama!! Cevabımı buna uygun şekilde güncelleyeceğim, teşekkür ederim @ChaseMedallion
joshcomley

Bu çözümü en çok sevdim, sunulan hüner olanlar sadece [Flags]mantıklı tamsayı değerleri varsa çalışır .
MrLore

17

Kanonik cevap şu olurdu Enum.IsDefined, ancak bu bir: sıkı bir döngüde kullanılırsa biraz yavaş ve b: [Flags]numaralandırmalar için yararlı değildir .

Şahsen, bu konuda endişelenmeyi bırakıp sadece switchuygun bir şekilde hatırlamak:

  • her şeyi tanımamanız (ve sadece bir şey yapmamanız) uygunsa, bir şey eklemeyin default:(veya default:nedenini açıklayan boş bir şey yapmayın )
  • makul bir varsayılan davranış varsa, bunu default:
  • aksi halde, tanıdıklarınızı kullanın ve geri kalanı için bir istisna atın:

Şöyle ki:

switch(someflag) {
    case TriBool.Yes:
        DoSomething();
        break;
    case TriBool.No:
        DoSomethingElse();
        break;
    case TriBool.FileNotFound:
        DoSomethingOther();
        break;
    default:
        throw new ArgumentOutOfRangeException("someflag");
}

[Bayraklar] enum'lara aşina değil ve performans bir sorun değildir, bu nedenle cevabınız enumların neden ilk başta icat edildiğine benziyor;) "noktalarınıza" ya da ne denirse bakmak, orada bir noktaya sahip olmalısınız . Bahse girerim onları hiçbir şey için alamazsınız, ancak bir enum savunmasında 257 değerin bulunduğu yapılandırma dosyasını okuma durumunu düşünün. Bırakın düzinelerce başka numaralandırma. Çok sayıda vaka satırı olurdu ...
char m

@matti - kulağa aşırı bir örnek gibi geliyor; Deserialization zaten uzman bir alandır - çoğu serileştirme motoru ücretsiz numaralandırma doğrulaması sunar.
Marc Gravell

@matti - bir yan notta; Ben kendi cevaplarına göre cevapları tedavi söyleyebilirim. Bazen işleri tamamen yanlış anlıyorum ve "temsilci 17" olan biri de aynı derecede mükemmel bir cevap verebilir.
Marc Gravell

Anahtar yanıtı hızlıdır, ancak genel değildir.
Eldritch Conundrum

8

kullanın:

Enum.IsDefined ( typeof ( Enum ), EnumValue );


4

Bununla başa çıkmak için C # Yemek Kitabı'ndan bu çözümü[Flags] de kullanabilirsiniz :

İlk olarak, ALLnumaralandırmanıza yeni bir değer ekleyin :

[Flags]
enum Language
{
    CSharp = 1, VBNET = 2, VB6 = 4, 
    All = (CSharp | VBNET | VB6)
}

Ardından, değerin bulunup bulunmadığını kontrol edin ALL:

public bool HandleFlagsEnum(Language language)
{
    if ((language & Language.All) == language)
    {
        return (true);
    }
    else
    {
        return (false);
    }
}

2

Bunu yapmanın bir yolu döküm ve dizge dönüşüm numaralandırma güvenmek olacaktır. İnt bir Enum türüne dönüştürülürken int, karşılık gelen bir enum değerine dönüştürülür veya int için enum değeri tanımlanmamışsa, sonuç olarak enum yalnızca bir değer olarak int içerir.

enum NetworkStatus{
  Unknown=0,
  Active,
  Slow
}

int statusCode=2;
NetworkStatus netStatus = (NetworkStatus) statusCode;
bool isDefined = netStatus.ToString() != statusCode.ToString();

Herhangi bir kenar kasa için test edilmemiştir.


1

Diğerlerinin söylediği gibi, enum için süslenmiş bir numaralandırma için geçerli bir bit bayrağı kombinasyonunuz olsa bile Enum.IsDefineddönerfalseFlagsAttribute .

Ne yazık ki, geçerli bit bayrakları için true döndüren bir yöntem oluşturmanın tek yolu biraz uzun:

public static bool ValidateEnumValue<T>(T value) where T : Enum
{
    // Check if a simple value is defined in the enum.
    Type enumType = typeof(T);
    bool valid = Enum.IsDefined(enumType, value);
    // For enums decorated with the FlagsAttribute, allow sets of flags.
    if (!valid && enumType.GetCustomAttributes(typeof(FlagsAttribute), false)?.Any() == true)
    {
        long mask = 0;
        foreach (object definedValue in Enum.GetValues(enumType))
            mask |= Convert.ToInt64(definedValue);
        long longValue = Convert.ToInt64(value);
        valid = (mask & longValue) == longValue;
    }
    return valid;
}

Sonuçlarını GetCustomAttributebir sözlükte önbelleğe almak isteyebilirsiniz :

private static readonly Dictionary<Type, bool> _flagEnums = new Dictionary<Type, bool>();
public static bool ValidateEnumValue<T>(T value) where T : Enum
{
    // Check if a simple value is defined in the enum.
    Type enumType = typeof(T);
    bool valid = Enum.IsDefined(enumType, value);
    if (!valid)
    {
        // For enums decorated with the FlagsAttribute, allow sets of flags.
        if (!_flagEnums.TryGetValue(enumType, out bool isFlag))
        {
            isFlag = enumType.GetCustomAttributes(typeof(FlagsAttribute), false)?.Any() == true;
            _flagEnums.Add(enumType, isFlag);
        }
        if (isFlag)
        {
            long mask = 0;
            foreach (object definedValue in Enum.GetValues(enumType))
                mask |= Convert.ToInt64(definedValue);
            long longValue = Convert.ToInt64(value);
            valid = (mask & longValue) == longValue;
        }
    }
    return valid;
}

Yukarıdaki kodun yalnızca C # 7.3'ten beri kullanılabilen yeni Enumkısıtlamayı kullandığını unutmayın T. object valueEski sürümlerde bir geçirmeniz ve çağırmanız GetType()gerekir.

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.