Odpowiedzi:
Z ciągu:
YourEnum foo = (YourEnum) Enum.Parse(typeof(YourEnum), yourString);
// The foo.ToString().Contains(",") check is necessary for enumerations marked with an [Flags] attribute
if (!Enum.IsDefined(typeof(YourEnum), foo) && !foo.ToString().Contains(","))
{
throw new InvalidOperationException($"{yourString} is not an underlying value of the YourEnum enumeration.")
}
Od int:
YourEnum foo = (YourEnum)yourInt;
Aktualizacja:
Z numeru możesz także
YourEnum foo = (YourEnum)Enum.ToObject(typeof(YourEnum) , yourInt);
var result = Enum.TryParse(yourString, out yourEnum)
obecnie (i sprawdzać wynik, aby ustalić, czy konwersja nie powiodła się).
Enum.Parse
rozróżniać małe i wielkie litery, dodając true
wartość parametru do wywołania:YourEnum foo = (YourEnum) Enum.Parse(typeof(YourEnum), yourString, true);
Po prostu rzuć to:
MyEnum e = (MyEnum)3;
Możesz sprawdzić, czy jest w zasięgu za pomocą Enum.IsDefined :
if (Enum.IsDefined(typeof(MyEnum), 3)) { ... }
Enum.IsDefined
, należy pamiętać, że może to być niebezpieczne: msdn.microsoft.com/en-us/library/ms229025(VS.90).aspx
IsDefined
sprawdzania wartości wejściowych narażasz się na ryzyko, że ludzie dodadzą nowe wartości wyliczenia później, co przejdzie IsDefined
kontrolę (od czasu nowej wartość istnieje w nowym kodzie), ale może nie działać z oryginalnym kodem, który napisałeś. Dlatego bezpieczniej jest jawnie określić wartości wyliczeniowe, które twój kod jest w stanie obsłużyć.
Alternatywnie użyj metody rozszerzenia zamiast jednowarstwowej:
public static T ToEnum<T>(this string enumString)
{
return (T) Enum.Parse(typeof (T), enumString);
}
Stosowanie:
Color colorEnum = "Red".ToEnum<Color>();
LUB
string color = "Red";
var colorEnum = color.ToEnum<Color>();
System.String
wydaje się jak zanieczyszczenie przestrzeni nazw
Myślę, że aby uzyskać pełną odpowiedź, ludzie muszą wiedzieć, jak wyliczenia działają wewnętrznie w .NET.
Jak działają rzeczy
Wyliczenie w .NET to struktura, która odwzorowuje zestaw wartości (pól) na typ podstawowy (domyślnie jest int
). Możesz jednak wybrać typ całki, na który odwzorowuje twój enum:
public enum Foo : short
W tym przypadku wyliczenie jest odwzorowane na short
typ danych, co oznacza, że zostanie zapisane w pamięci jako krótkie i będzie zachowywać się jak krótkie po rzutowaniu i użyciu.
Jeśli spojrzysz na to z punktu widzenia IL, wyliczenie (normalne, int) wygląda następująco:
.class public auto ansi serializable sealed BarFlag extends System.Enum
{
.custom instance void System.FlagsAttribute::.ctor()
.custom instance void ComVisibleAttribute::.ctor(bool) = { bool(true) }
.field public static literal valuetype BarFlag AllFlags = int32(0x3fff)
.field public static literal valuetype BarFlag Foo1 = int32(1)
.field public static literal valuetype BarFlag Foo2 = int32(0x2000)
// and so on for all flags or enum values
.field public specialname rtspecialname int32 value__
}
Należy zwrócić uwagę na to, że value__
są przechowywane oddzielnie od wartości wyliczeniowych. W przypadku Foo
powyższego wyliczenia typem value__
jest int16. Zasadniczo oznacza to, że możesz przechowywać, co chcesz, w wyliczeniu, o ile typy są zgodne .
W tym miejscu chciałbym wskazać, że System.Enum
jest to typ wartości, co w zasadzie oznacza, że BarFlag
zajmie 4 bajty w pamięci i Foo
zajmie 2 - np. Rozmiar typu bazowego (jest to w rzeczywistości bardziej skomplikowane, ale Hej...).
Odpowiedź
Jeśli więc masz liczbę całkowitą, którą chcesz zmapować do wyliczenia, środowisko wykonawcze musi tylko zrobić 2 rzeczy: skopiować 4 bajty i nazwać to czymś innym (nazwa wyliczenia). Kopiowanie jest niejawne, ponieważ dane są przechowywane jako typ wartości - oznacza to po prostu, że jeśli używasz niezarządzanego kodu, możesz po prostu zamieniać wyliczenia i liczby całkowite bez kopiowania danych.
Dla bezpieczeństwa uważam, że najlepszą praktyką jest wiedzieć, że typy bazowe są takie same lub domyślnie konwertowalne oraz aby zapewnić istnienie wartości wyliczeniowych (domyślnie nie są sprawdzane!).
Aby zobaczyć, jak to działa, wypróbuj następujący kod:
public enum MyEnum : int
{
Foo = 1,
Bar = 2,
Mek = 5
}
static void Main(string[] args)
{
var e1 = (MyEnum)5;
var e2 = (MyEnum)6;
Console.WriteLine("{0} {1}", e1, e2);
Console.ReadLine();
}
Pamiętaj, że rzutowanie e2
również działa! Z powyższego punktu widzenia kompilatora ma to sens: value__
pole jest po prostu wypełnione albo 5 albo 6, a kiedy Console.WriteLine
wywołania ToString()
, nazwa e1
zostaje rozwiązana, a nazwa e2
nie.
Jeśli nie jest to zamierzone, użyj, Enum.IsDefined(typeof(MyEnum), 6)
aby sprawdzić, czy wartość rzutujesz mapy do zdefiniowanego wyliczenia.
Zauważ też, że wyrażam się jasno o podstawowym typie wyliczenia, nawet jeśli kompilator faktycznie to sprawdza. Robię to, aby upewnić się, że nie spotka mnie żadna niespodzianka. Aby zobaczyć te niespodzianki w akcji, możesz użyć następującego kodu (faktycznie widziałem, że tak się często dzieje w kodzie bazy danych):
public enum MyEnum : short
{
Mek = 5
}
static void Main(string[] args)
{
var e1 = (MyEnum)32769; // will not compile, out of bounds for a short
object o = 5;
var e2 = (MyEnum)o; // will throw at runtime, because o is of type int
Console.WriteLine("{0} {1}", e1, e2);
Console.ReadLine();
}
int
! = short
, Wyrzuci (rozpakowanie nie powiedzie się). Jeśli to zrobisz object o = (short)5;
, będzie działać, ponieważ wtedy typy będą pasować. Nie chodzi o zakres, ale o typ.
Weź następujący przykład:
int one = 1;
MyEnum e = (MyEnum)one;
Korzystam z tego fragmentu kodu, aby rzucić int do mojego wyliczenia:
if (typeof(YourEnum).IsEnumDefined(valueToCast)) return (YourEnum)valueToCast;
else { //handle it here, if its not defined }
Uważam to za najlepsze rozwiązanie.
Poniżej znajduje się niezła klasa użyteczności dla Enums
public static class EnumHelper
{
public static int[] ToIntArray<T>(T[] value)
{
int[] result = new int[value.Length];
for (int i = 0; i < value.Length; i++)
result[i] = Convert.ToInt32(value[i]);
return result;
}
public static T[] FromIntArray<T>(int[] value)
{
T[] result = new T[value.Length];
for (int i = 0; i < value.Length; i++)
result[i] = (T)Enum.ToObject(typeof(T),value[i]);
return result;
}
internal static T Parse<T>(string value, T defaultValue)
{
if (Enum.IsDefined(typeof(T), value))
return (T) Enum.Parse(typeof (T), value);
int num;
if(int.TryParse(value,out num))
{
if (Enum.IsDefined(typeof(T), num))
return (T)Enum.ToObject(typeof(T), num);
}
return defaultValue;
}
}
W przypadku wartości liczbowych jest to bezpieczniejsze, ponieważ zwróci obiekt bez względu na wszystko:
public static class EnumEx
{
static public bool TryConvert<T>(int value, out T result)
{
result = default(T);
bool success = Enum.IsDefined(typeof(T), value);
if (success)
{
result = (T)Enum.ToObject(typeof(T), value);
}
return success;
}
}
Jeśli jesteś gotowy na 4.0 .NET Framework, dostępna jest nowa funkcja Enum.TryParse (), która jest bardzo przydatna i dobrze współpracuje z atrybutem [Flagi]. Zobacz Enum.TryParse Method (String, TEnum%)
Jeśli masz liczbę całkowitą, która działa jak maska bitowa i może reprezentować jedną lub więcej wartości w wyliczeniu [Flagi], możesz użyć tego kodu, aby przeanalizować poszczególne wartości flagi na liście:
for (var flagIterator = 0; flagIterator < 32; flagIterator++)
{
// Determine the bit value (1,2,4,...,Int32.MinValue)
int bitValue = 1 << flagIterator;
// Check to see if the current flag exists in the bit mask
if ((intValue & bitValue) != 0)
{
// If the current flag exists in the enumeration, then we can add that value to the list
// if the enumeration has that flag defined
if (Enum.IsDefined(typeof(MyEnum), bitValue))
Console.WriteLine((MyEnum)bitValue);
}
}
Zauważ, że zakłada to, że podstawowym typem enum
jest 32-bitowa liczba całkowita ze znakiem. Gdyby był to inny typ liczbowy, musiałbyś zmienić kod 32, aby odzwierciedlić bity tego typu (lub programowo wyprowadzić go za pomocą Enum.GetUnderlyingType()
)
Jest to metoda bezpiecznej konwersji z uwzględnieniem wyliczania flag:
public static bool TryConvertToEnum<T>(this int instance, out T result)
where T: Enum
{
var enumType = typeof (T);
var success = Enum.IsDefined(enumType, instance);
if (success)
{
result = (T)Enum.ToObject(enumType, instance);
}
else
{
result = default(T);
}
return success;
}
Enum
zamiast struct
, co oznacza, że nie musimy polegać na kontroli środowiska wykonawczego!
Aby przekonwertować ciąg znaków na ENUM lub int na stałą ENUM, musimy użyć funkcji Enum.Parse. Oto film z youtube https://www.youtube.com/watch?v=4nhx4VwdRDk, który faktycznie pokazuje ciąg znaków i to samo dotyczy int.
Kod wygląda tak, jak pokazano poniżej, gdzie „czerwony” to ciąg znaków, a „MyColors” to kolor ENUM, który ma stałe kolorów.
MyColors EnumColors = (MyColors)Enum.Parse(typeof(MyColors), "Red");
Nieco uciekam od pierwotnego pytania, ale znalazłem odpowiedź na pytanie Przepełnienie stosu Uzyskaj wartość int z enum przydaje się. Utwórz klasę statyczną o public const int
właściwościach, która pozwoli ci łatwo zebrać razem kilka powiązanych int
stałych, a następnie nie musisz ich rzutować int
podczas ich używania.
public static class Question
{
public static readonly int Role = 2;
public static readonly int ProjectFunding = 3;
public static readonly int TotalEmployee = 4;
public static readonly int NumberOfServers = 5;
public static readonly int TopBusinessConcern = 6;
}
Oczywiście niektóre funkcje typu wyliczania zostaną utracone, ale do przechowywania szeregu stałych identyfikatora bazy danych wydaje się to dość uporządkowane rozwiązanie.
To analizuje liczby całkowite lub ciągi do docelowego wyliczenia z częściowym dopasowaniem w dot.NET 4.0 przy użyciu ogólnych, takich jak w klasie użyteczności Tawani powyżej. Używam go do konwersji zmiennych przełączników wiersza polecenia, które mogą być niekompletne. Ponieważ wyliczenie nie może mieć wartości null, należy logicznie podać wartość domyślną. Można to nazwać tak:
var result = EnumParser<MyEnum>.Parse(valueToParse, MyEnum.FirstValue);
Oto kod:
using System;
public class EnumParser<T> where T : struct
{
public static T Parse(int toParse, T defaultVal)
{
return Parse(toParse + "", defaultVal);
}
public static T Parse(string toParse, T defaultVal)
{
T enumVal = defaultVal;
if (defaultVal is Enum && !String.IsNullOrEmpty(toParse))
{
int index;
if (int.TryParse(toParse, out index))
{
Enum.TryParse(index + "", out enumVal);
}
else
{
if (!Enum.TryParse<T>(toParse + "", true, out enumVal))
{
MatchPartialName(toParse, ref enumVal);
}
}
}
return enumVal;
}
public static void MatchPartialName(string toParse, ref T enumVal)
{
foreach (string member in enumVal.GetType().GetEnumNames())
{
if (member.ToLower().Contains(toParse.ToLower()))
{
if (Enum.TryParse<T>(member + "", out enumVal))
{
break;
}
}
}
}
}
FYI: Pytanie dotyczyło liczb całkowitych, o których nikt nie wspomniał, które również zostaną jawnie przekonwertowane w Enum.TryParse ()
Z ciągu: (Enum.Parse jest nieaktualny, użyj Enum.TryParse)
enum Importance
{}
Importance importance;
if (Enum.TryParse(value, out importance))
{
}
Poniżej znajduje się nieco lepsza metoda rozszerzenia
public static string ToEnumString<TEnum>(this int enumValue)
{
var enumString = enumValue.ToString();
if (Enum.IsDefined(typeof(TEnum), enumValue))
{
enumString = ((TEnum) Enum.ToObject(typeof (TEnum), enumValue)).ToString();
}
return enumString;
}
W moim przypadku musiałem zwrócić wyliczenie z usługi WCF. Potrzebowałem także przyjaznej nazwy, nie tylko enum.ToString ().
Oto moja klasa WCF.
[DataContract]
public class EnumMember
{
[DataMember]
public string Description { get; set; }
[DataMember]
public int Value { get; set; }
public static List<EnumMember> ConvertToList<T>()
{
Type type = typeof(T);
if (!type.IsEnum)
{
throw new ArgumentException("T must be of type enumeration.");
}
var members = new List<EnumMember>();
foreach (string item in System.Enum.GetNames(type))
{
var enumType = System.Enum.Parse(type, item);
members.Add(
new EnumMember() { Description = enumType.GetDescriptionValue(), Value = ((IConvertible)enumType).ToInt32(null) });
}
return members;
}
}
Oto metoda Extension, która pobiera Opis z Enum.
public static string GetDescriptionValue<T>(this T source)
{
FieldInfo fileInfo = source.GetType().GetField(source.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fileInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
{
return attributes[0].Description;
}
else
{
return source.ToString();
}
}
Realizacja:
return EnumMember.ConvertToList<YourType>();
Nie wiem już, skąd biorę część tego rozszerzenia enum, ale pochodzi ono z przepełnienia stosu. Przepraszam za to! Ale wziąłem ten i zmodyfikowałem dla wyliczeń za pomocą Flags. W przypadku wyliczeń z flagami zrobiłem to:
public static class Enum<T> where T : struct
{
private static readonly IEnumerable<T> All = Enum.GetValues(typeof (T)).Cast<T>();
private static readonly Dictionary<int, T> Values = All.ToDictionary(k => Convert.ToInt32(k));
public static T? CastOrNull(int value)
{
T foundValue;
if (Values.TryGetValue(value, out foundValue))
{
return foundValue;
}
// For enums with Flags-Attribut.
try
{
bool isFlag = typeof(T).GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0;
if (isFlag)
{
int existingIntValue = 0;
foreach (T t in Enum.GetValues(typeof(T)))
{
if ((value & Convert.ToInt32(t)) > 0)
{
existingIntValue |= Convert.ToInt32(t);
}
}
if (existingIntValue == 0)
{
return null;
}
return (T)(Enum.Parse(typeof(T), existingIntValue.ToString(), true));
}
}
catch (Exception)
{
return null;
}
return null;
}
}
Przykład:
[Flags]
public enum PetType
{
None = 0, Dog = 1, Cat = 2, Fish = 4, Bird = 8, Reptile = 16, Other = 32
};
integer values
1=Dog;
13= Dog | Fish | Bird;
96= Other;
128= Null;
Powinieneś wbudować relaksację dopasowaną do typu, aby być bardziej niezawodnym.
public static T ToEnum<T>(dynamic value)
{
if (value == null)
{
// default value of an enum is the object that corresponds to
// the default value of its underlying type
// https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/default-values-table
value = Activator.CreateInstance(Enum.GetUnderlyingType(typeof(T)));
}
else if (value is string name)
{
return (T)Enum.Parse(typeof(T), name);
}
return (T)Enum.ToObject(typeof(T),
Convert.ChangeType(value, Enum.GetUnderlyingType(typeof(T))));
}
Przypadek testowy
[Flags]
public enum A : uint
{
None = 0,
X = 1 < 0,
Y = 1 < 1
}
static void Main(string[] args)
{
var value = EnumHelper.ToEnum<A>(7m);
var x = value.HasFlag(A.X); // true
var y = value.HasFlag(A.Y); // true
var value2 = EnumHelper.ToEnum<A>("X");
var value3 = EnumHelper.ToEnum<A>(null);
Console.ReadKey();
}
Różne sposoby przesyłania do iz Enum
enum orientation : byte
{
north = 1,
south = 2,
east = 3,
west = 4
}
class Program
{
static void Main(string[] args)
{
orientation myDirection = orientation.north;
Console.WriteLine(“myDirection = {0}”, myDirection); //output myDirection =north
Console.WriteLine((byte)myDirection); //output 1
string strDir = Convert.ToString(myDirection);
Console.WriteLine(strDir); //output north
string myString = “north”; //to convert string to Enum
myDirection = (orientation)Enum.Parse(typeof(orientation),myString);
}
}
Może pomóc ci przekonwertować dowolne dane wejściowe na żądane przez użytkownika wyliczenie . Załóżmy, że masz wyliczenie jak poniżej, które domyślnie int . Dodaj wartość domyślną na początku swojego wyliczenia. Który jest używany w pomocnikach medthod, gdy nie znaleziono dopasowania z wartością wejściową.
public enum FriendType
{
Default,
Audio,
Video,
Image
}
public static class EnumHelper<T>
{
public static T ConvertToEnum(dynamic value)
{
var result = default(T);
var tempType = 0;
//see Note below
if (value != null &&
int.TryParse(value.ToString(), out tempType) &&
Enum.IsDefined(typeof(T), tempType))
{
result = (T)Enum.ToObject(typeof(T), tempType);
}
return result;
}
}
NB: Tutaj próbuję parsować wartość na int, ponieważ enum jest domyślnie int Jeśli zdefiniujesz enum jak ten, który jest typem bajtu .
public enum MediaType : byte
{
Default,
Audio,
Video,
Image
}
Musisz zmienić parsowanie w metodzie pomocniczej z
int.TryParse(value.ToString(), out tempType)
do
byte.TryParse(value.ToString(), out tempType)
Sprawdzam moją metodę pod kątem następujących danych wejściowych
EnumHelper<FriendType>.ConvertToEnum(null);
EnumHelper<FriendType>.ConvertToEnum("");
EnumHelper<FriendType>.ConvertToEnum("-1");
EnumHelper<FriendType>.ConvertToEnum("6");
EnumHelper<FriendType>.ConvertToEnum("");
EnumHelper<FriendType>.ConvertToEnum("2");
EnumHelper<FriendType>.ConvertToEnum(-1);
EnumHelper<FriendType>.ConvertToEnum(0);
EnumHelper<FriendType>.ConvertToEnum(1);
EnumHelper<FriendType>.ConvertToEnum(9);
przepraszam za mój angielski
Oto metoda rozszerzenia, która przesyła Int32
do Enum
.
Uwzględnia flagi bitowe, nawet jeśli wartość jest wyższa niż maksymalna możliwa. Na przykład, jeśli masz wyliczenie z możliwościami 1 , 2 i 4 , ale int wynosi 9 , rozumie to jako 1 przy braku 8 . Umożliwia to aktualizację danych przed aktualizacją kodu.
public static TEnum ToEnum<TEnum>(this int val) where TEnum : struct, IComparable, IFormattable, IConvertible
{
if (!typeof(TEnum).IsEnum)
{
return default(TEnum);
}
if (Enum.IsDefined(typeof(TEnum), val))
{//if a straightforward single value, return that
return (TEnum)Enum.ToObject(typeof(TEnum), val);
}
var candidates = Enum
.GetValues(typeof(TEnum))
.Cast<int>()
.ToList();
var isBitwise = candidates
.Select((n, i) => {
if (i < 2) return n == 0 || n == 1;
return n / 2 == candidates[i - 1];
})
.All(y => y);
var maxPossible = candidates.Sum();
if (
Enum.TryParse(val.ToString(), out TEnum asEnum)
&& (val <= maxPossible || !isBitwise)
){//if it can be parsed as a bitwise enum with multiple flags,
//or is not bitwise, return the result of TryParse
return asEnum;
}
//If the value is higher than all possible combinations,
//remove the high imaginary values not accounted for in the enum
var excess = Enumerable
.Range(0, 32)
.Select(n => (int)Math.Pow(2, n))
.Where(n => n <= val && n > 0 && !candidates.Contains(n))
.Sum();
return Enum.TryParse((val - excess).ToString(), out asEnum) ? asEnum : default(TEnum);
}
prosty i przejrzysty sposób na rzutowanie int na wyliczenie w c #:
public class Program
{
public enum Color : int
{
Blue = 0,
Black = 1,
Green = 2,
Gray = 3,
Yellow =4
}
public static void Main(string[] args)
{
//from string
Console.WriteLine((Color) Enum.Parse(typeof(Color), "Green"));
//from int
Console.WriteLine((Color)2);
//From number you can also
Console.WriteLine((Color)Enum.ToObject(typeof(Color) ,2));
}
}
Wystarczy użyć jawnej konwersji Cast int na enum lub enum na int
class Program
{
static void Main(string[] args)
{
Console.WriteLine((int)Number.three); //Output=3
Console.WriteLine((Number)3);// Outout three
Console.Read();
}
public enum Number
{
Zero = 0,
One = 1,
Two = 2,
three = 3
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace SamplePrograme
{
public class Program
{
public enum Suit : int
{
Spades = 0,
Hearts = 1,
Clubs = 2,
Diamonds = 3
}
public static void Main(string[] args)
{
//from string
Console.WriteLine((Suit) Enum.Parse(typeof(Suit), "Clubs"));
//from int
Console.WriteLine((Suit)1);
//From number you can also
Console.WriteLine((Suit)Enum.ToObject(typeof(Suit) ,1));
}
}
}
Po prostu lubisz poniżej:
int intToCast = 1;
TargetEnum f = (TargetEnum) intToCast ;
Aby upewnić się, że rzucisz tylko prawidłowe wartości i że możesz wrzucić wyjątek:
int intToCast = 1;
if (Enum.IsDefined(typeof(TargetEnum), intToCast ))
{
TargetEnum target = (TargetEnum)intToCast ;
}
else
{
// Throw your exception.
}
Pamiętaj, że korzystanie z IsDefined jest kosztowne, a nawet więcej niż tylko rzutowanie, więc decyzja o jego użyciu zależy od implementacji.
Możesz użyć metody rozszerzenia.
public static class Extensions
{
public static T ToEnum<T>(this string data) where T : struct
{
if (!Enum.TryParse(data, true, out T enumVariable))
{
if (Enum.IsDefined(typeof(T), enumVariable))
{
return enumVariable;
}
}
return default;
}
public static T ToEnum<T>(this int data) where T : struct
{
return (T)Enum.ToObject(typeof(T), data);
}
}
użyj jak poniżej kodu
wyliczenie:
public enum DaysOfWeeks
{
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6,
Sunday = 7,
}
Stosowanie :
string Monday = "Mon";
int Wednesday = 3;
var Mon = Monday.ToEnum<DaysOfWeeks>();
var Wed = Wednesday.ToEnum<DaysOfWeeks>();
YourEnum
jest dynamiczny i będzie znany tylko w środowisku wykonawczym, a to, czego chcę, to konwersjaEnum
?