Pytanie Oddaj int do wyliczenia w C #


Jak można int być rzuconym na enum w języku C #?


2571
2017-08-27 03:58


pochodzenie


Na odwrót: get-int-value-from-enum - nawfal
Darmowe 5-minutowe wideo obejmujące najlepsze z poniższych odpowiedzi: app.deviq.com/courses/how-do-i-convert-int-to-enum-in-c- sharp - ssmith


Odpowiedzi:


Z ciągu znaków:

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 też

YourEnum foo = (YourEnum)Enum.ToObject(typeof(YourEnum) , yourInt);

3123
2017-08-27 03:59



@FlySwat, co jeśli YourEnum jest dynamiczny i będzie znany tylko w czasie wykonywania, a ja chcę go przekonwertować Enum? - Shimmy
Pamiętaj, że Enum.Parse NIE będzie działał, jeśli Twój kod zostanie zaciemniony. W czasie wykonywania po zaciemnieniu ciąg jest porównywany z nazwami wyliczanymi, a na tym etapie nazwy wyrażeń nie są tym, czego można by się spodziewać. Twoja analiza zakończy się niepowodzeniem tam, gdzie się udało. - jropella
STRZEC SIĘ Jeśli użyjesz powyższej składni "z łańcucha" i podasz niepoprawny łańcuch, który jest liczbą (np. "2342342" - zakładając, że nie jest to wartość twojego wyliczenia), faktycznie pozwoli na to bez rzucania błędu! Twoje wyliczenie będzie miało tę wartość (2342342), nawet jeśli nie jest to poprawny wybór w samym wyliczeniu. - JoeCool
Myślę, że ta odpowiedź jest nieco przestarzała. Dla struny, naprawdę powinieneś używać var result = Enum.TryParse(yourString, out yourEnum) w dzisiejszych czasach (i sprawdzanie wyniku w celu ustalenia, czy konwersja się nie udała). - Justin T Conroy
Możliwe jest również Enum.Parse nie rozróżnia wielkości liter przez dodanie a true wartość parametru do połączenia: YourEnum foo = (YourEnum) Enum.Parse(typeof(YourEnum), yourString, true); - Erik Schierboom


Wystarczy go rzucić:

MyEnum e = (MyEnum)3;

Możesz sprawdzić, czy jest w zasięgu przy użyciu Enum.IsDefined:

if (Enum.IsDefined(typeof(MyEnum), 3)) { ... }

721
2017-08-27 04:01



Uważaj, nie możesz użyć metody Enum.IsDefined, jeśli używasz atrybutu Flags, a wartość jest kombinacją flag, na przykład: Keys.L | Keys.Control - dtroy
Jeżeli chodzi o Enum.IsDefined, pamiętaj, że może to być niebezpieczne: msdn.microsoft.com/en-us/library/ms229025 (VS.90).aspx - adrian
Z drugiej strony, jeśli sprawdzę poprawność danych wejściowych na podstawie enumu, który zapewnia biblioteka, przed wysłaniem go do samej biblioteki ... - Alexander
Wolę tę definicję: "Zwraca wskazanie, czy w określonym wyliczeniu istnieje stała o określonej wartości" od MSDN - Pap
... Ponieważ twoja definicja może wprowadzać w błąd, ponieważ mówisz: "... sprawdź, czy jest w zasięgu ..." co oznacza zakres liczb z początkowymi i końcowymi limitami ... - Pap


Alternatywnie, użyj metody rozszerzającej zamiast jednolinijki:

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

198
2017-11-11 13:27



Aby przetworzyć dane wprowadzane przez użytkownika, dobrym pomysłem jest wywołanie przeciążenia programu Enum.Parse, które pozwala ci określić, że w porównaniu NIE ma znaczenia wielkość liter (tzn. Użytkownik wpisujący "czerwony" (małe litery) spowodowałby awarię powyższego kodu bez tej zmiany .) - BrainSlugs83
Fajnie, ale to nie jest pytanie. - nawfal
Handy, ale pytanie w szczególności pyta o ints. - BJury
działa to również, jeśli ciąg jest liczbą całkowitą, np. "2" - TruthOf42
Spowoduje to zgłoszenie wyjątku, jeśli enumString ma wartość null (wczoraj miał podobny problem). Rozważ użycie TryParse zamiast Parse. TryParse sprawdzi również, czy T jest typem enum - Justin


Myślę, że aby otrzymać kompletną odpowiedź, ludzie muszą wiedzieć, w jaki sposób enum działa wewnętrznie w .NET.

Jak działają rzeczy

Wyliczenie w .NET jest strukturą, która odwzorowuje zbiór wartości (pól) na typ podstawowy (domyślnie jest to int). Możesz jednak wybrać typ całki, do którego wylicza się twój wylicznik:

public enum Foo : short

W tym przypadku enum jest mapowane na short typ danych, co oznacza, że ​​będzie przechowywany w pamięci jako krótki i będzie zachowywał się jak krótki podczas jego przesyłania i używania.

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

Na co należy zwrócić tutaj uwagę, to że value__ jest przechowywany oddzielnie od wartości wyliczonych. W przypadku wyliczenia Foo powyżej, typ value__ jest int16. To w zasadzie oznacza, że ​​możesz przechowywać wszystko, co chcesz w wyliczeniu, tak długo, jak typy się zgadzają.

W tym miejscu chciałbym to podkreślić System.Enum jest typem wartości, który w zasadzie oznacza to BarFlag zajmie 4 bajty pamięci i Foo zajmie 2 - np. rozmiar podstawowego typu (jest to w rzeczywistości bardziej skomplikowane, ale hej ...).

Odpowiedź

Tak więc, jeśli masz liczbę całkowitą, którą chcesz odwzorować na wyliczenie, środowisko wykonawcze musi wykonać tylko 2 czynności: skopiuj 4 bajty i nazwij je czymś innym (nazwą wyliczenia). Kopiowanie jest niejawne, ponieważ dane są przechowywane jako typ wartości - w zasadzie oznacza to, że jeśli używasz niezarządzanego kodu, możesz po prostu zamienić enumy i liczby całkowite bez kopiowania danych.

Aby było to bezpieczne, myślę, że jest to najlepsza praktyka wiedzieć, że podstawowe typy są takie same lub niejawnie wymienialne i aby zapewnić wartości wyliczeniowe (nie są one domyślnie zaznaczone!).

Aby zobaczyć, jak to działa, wypróbuj poniższy 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 odlewanie do e2 działa również! Z powyższej perspektywy kompilatora ma to sens: value__ pole jest po prostu wypełnione 5 lub 6 i kiedy Console.WriteLine połączenia ToString(), Nazwa e1 jest rozwiązany, gdy nazwa e2 nie jest.

Jeśli nie jest to zamierzone, użyj Enum.IsDefined(typeof(MyEnum), 6) aby sprawdzić, czy wartość rzucania map do zdefiniowanego wyliczenia.

Zauważ też, że mówię wprost o podstawowym typie wyliczenia, mimo że kompilator faktycznie to sprawdza. Robię to, aby upewnić się, że nie napotkasz żadnych niespodzianek na drodze. Aby zobaczyć te niespodzianki w działaniu, możesz użyć następującego kodu (faktycznie widziałem, że tak wiele dzieje się 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();
}

116
2018-04-03 07:39



Zdaję sobie sprawę, że jest to stary post, ale jak zdobyć ten poziom wiedzy w języku c #? Czy to czyta ze specyfikacji C #? - Rolan
@Rolan Czasami żałuję, że więcej ludzi o to nie poprosi. :-) Szczerze mówiąc, tak naprawdę nie wiem; Próbuję zrozumieć, jak działają rzeczy i uzyskać informacje, gdziekolwiek mogę je zdobyć. Czytałem standard C #, ale także regularnie dekompilowałem kod za pomocą Reflectora (nawet często patrzę na kod asemblera x86) i robię mnóstwo małych eksperymentów. W tym przypadku pomaga również znajomość innych języków; Robię CS od około 30 lat, i w pewnym momencie pewne rzeczy stają się "logiczne" - np. enum powinno być typu integralnego, ponieważ w przeciwnym razie interop zostanie przerwany (lub twoje wyniki spadną do dna). - atlaste
Uważam, że kluczem do prawidłowej inżynierii oprogramowania jest wiedza o tym, jak działa. Dla mnie oznacza to, że jeśli napiszesz kawałek kodu, wiesz, jak to w przybliżeniu tłumaczy się na przykład. operacje procesora i pamięć pobiera / zapisuje. Jeśli zapytasz, jak dostać się do tego poziomu, proponuję zbudowanie mnóstwa małych przypadków testowych, dzięki czemu będą one bardziej wytrzymałe, spróbuj przewidzieć wynik za każdym razem i przetestuj je później (włącznie z dekompilacją itp.). Po zapoznaniu się ze wszystkimi szczegółami i wszystkimi charakterystykami, możesz sprawdzić, czy masz je w (nudnym) standardzie. Przynajmniej to byłoby moje podejście. - atlaste
Fantastyczna odpowiedź, dzięki! W twoim ostatnim przykładzie kodu, zgłasza wyjątek w czasie wykonywania, ponieważ o jest obiektem. Możesz rzucić zmienną int na krótki, o ile mieści się w krótkim zasięgu. - gravidThoughts
@gravidThoughts Dzięki. W rzeczywistości jest to operacja rozpakowywania, więc nie wykona żadnych niejawnych konwersji, takich jak te, które opisujesz. Przesyłanie jest czasami mylące w C #, jeśli nie znasz szczegółów ... W każdym razie, ponieważ int ! = short, wyrzuci (rozpakowanie nie powiedzie się). Jeśli zrobisz object o = (short)5;, zadziała, ponieważ wtedy typy będą pasować. Tu nie chodzi o zasięg, naprawdę chodzi o typ. - atlaste


Weź następujący przykład:

int one = 1;
MyEnum e = (MyEnum)one;

89
2017-08-27 04:00





Używam tego fragmentu kodu, aby oddać 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.


54
2017-10-21 10:05



Nie działa z wyliczaniem flag - Daniel Fisher lennybacon
to jest dobre. Byłem zaskoczony, że nie ma wyjątku podczas rzucania nieprawidłowej wartości do enum opartego na zamknięciu. - orion elenzil
To właściwie nie jest tak odmienne od najlepiej ocenianej odpowiedzi. Ta odpowiedź omawia również użycie Enum.IsDefined po tym, jak rzuciłeś ciąg znaków w typie Enum. Więc nawet jeśli ciąg został rzucony bez błędu, Enum.IsDefined nadal będzie go przechwytywać - mmcrae


Poniżej znajduje się ładowna klasa narzędziowa 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;
    }
}

44
2017-09-07 04:42





Jeśli jesteś gotowy na 4.0 .NETTO Framework, jest nowy Enum.TryParse () funkcja bardzo przydatna i dobrze się gra z atrybutem [Flagi]. Widzieć Metoda Enum.TryParse (String, TEnum%)


37
2017-11-01 14:58



Jest to przydatne podczas konwersji z ciągu znaków. Ale nie podczas konwersji z int. - CodesInChaos


W przypadku wartości liczbowych jest to bezpieczniejsze, ponieważ zwraca obiekt bez względu na:

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

37
2018-02-21 15:22



To nie działa z wyliczeniami flagi - Daniel Fisher lennybacon


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 do przeanalizowania poszczególnych wartości flag na liście:

for (var flagIterator = 0x1; flagIterator <= 0x80000000; flagIterator <<= 1)
{
    // Check to see if the current flag exists in the bit mask
    if ((intValue & flagIterator) != 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), flagIterator))
            ListOfEnumValues.Add((MyEnum)flagIterator);
    }
}

25
2018-04-13 20:13





Czasami masz obiekt do MyEnum rodzaj. Lubić

var MyEnumType = typeof(MyEnumType);

Następnie:

Enum.ToObject(typeof(MyEnum), 3)

22
2017-07-02 14:41