C’è un modo per verificare se int è legale enum in C #?

Ho letto alcuni post SO e sembra che manchi l’operazione di base.

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

Questo non causa eccezioni, è felice di memorizzare 78 . C’è un modo per convalidare un valore andando in un enum?

Dai un’occhiata a Enum.IsDefined

Uso:

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

Questo è l’esempio da quella pagina:

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

L’esempio mostra il seguente output:

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

Le soluzioni di cui sopra non si occupano delle situazioni di [Flags] .

La mia soluzione di seguito potrebbe presentare alcuni problemi di prestazioni (sono sicuro che si potrebbe ottimizzare in vari modi) ma in sostanza si dimostrerà sempre se un valore enum è valido o meno .

Si basa su tre presupposti:

  • I valori Enum in C # possono essere solo int , assolutamente nient’altro
  • I nomi Enum in C # devono iniziare con un carattere alfabetico
  • Nessun nome enum valido può essere associato a un segno meno: -

Chiamare ToString() su un enum restituisce il valore int se nessun enum (flag o non) è abbinato. Se un valore enum consentito è abbinato, stamperà il nome della corrispondenza (es).

Così:

 [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" 

Tenendo a mente queste due regole, possiamo supporre che se .NET Framework fa il suo lavoro correttamente, qualsiasi chiamata a un metodo ToString() di enum valido comporterà qualcosa che ha un carattere alfabetico come primo carattere:

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

Si potrebbe chiamarlo “hack”, ma i vantaggi sono che facendo affidamento sull’implementazione degli standard Enum e C # di Microsoft, non ci si basa sul proprio codice o controllo potenzialmente buggato. In situazioni in cui le prestazioni non sono eccezionalmente critiche, questo salverà molte dichiarazioni di switch sgradevoli o altri controlli!

modificare

Grazie a @ChaseMedallion per aver sottolineato che la mia implementazione originale non supportava valori negativi. Questo è stato risolto e vengono forniti i test.

E i test per il backup:

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

La risposta canonica sarebbe Enum.IsDefined , ma questo è un: un po ‘lento se usato in un ciclo stretto, e b: non utile per [Flags] enunciazioni [Flags] .

Personalmente, smetterei di preoccuparmene, e switch semplicemente, ricordando:

  • se è OK non riconoscere tutto (e non fare nulla), non aggiungere un default: (o avere un default: vuoto default: spiegare perché)
  • se esiste un comportamento predefinito sensato, inseriscilo come default:
  • altrimenti, gestisci quelli che conosci e fai un’eccezione per il resto:

Così:

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

Uso:

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

Usa Enum . È definito .

Per gestire [Flags] puoi utilizzare questa soluzione anche dal C # Cookbook :

Innanzitutto, aggiungi un nuovo valore ALL al tuo enum:

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

Quindi, controlla se il valore è ALL :

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

Un modo per farlo sarebbe quello di fare affidamento su casting e enum per convertire le stringhe. Quando si esegue il cast di un tipo Enum, l’int viene convertito in un valore enum corrispondente o l’enum risultante contiene solo int come valore se il valore enum non è definito per l’int.

 enum NetworkStatus{ Unknown=0, Active, Slow } int statusCode=2; NetworkStatus netStatus = (NetworkStatus) statusCode; bool isDefined = netStatus.ToString() != statusCode.ToString(); 

Non testato per nessun caso limite.