Buduję funkcję, aby rozszerzyć tę Enum.Parse
koncepcję
- Umożliwia parsowanie wartości domyślnej w przypadku, gdy nie zostanie znaleziona wartość Enum
- Rozróżnia małe i wielkie litery
Napisałem więc:
public static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
{
if (string.IsNullOrEmpty(value)) return defaultValue;
foreach (T item in Enum.GetValues(typeof(T)))
{
if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
}
return defaultValue;
}
Otrzymuję Ograniczenie błędu nie może być klasą specjalną System.Enum
.
W porządku, ale czy istnieje obejście pozwalające na generyczne wyliczenie, czy też będę musiał naśladować Parse
funkcję i przekazać typ jako atrybut, co wymusza brzydkie wymagania boksowania w twoim kodzie.
EDYCJA Wszystkie poniższe sugestie zostały bardzo docenione, dzięki.
Ustawiłem się (opuściłem pętlę, aby zachować rozróżnianie wielkości liter - używam tego podczas analizowania XML)
public static class EnumUtils
{
public static T ParseEnum<T>(string value, T defaultValue) where T : struct, IConvertible
{
if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");
if (string.IsNullOrEmpty(value)) return defaultValue;
foreach (T item in Enum.GetValues(typeof(T)))
{
if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
}
return defaultValue;
}
}
EDYCJA: (16 lutego 2015 r.) Julien Lebosquain opublikował niedawno wymuszone przez kompilator ogólne rozwiązanie bezpieczeństwa typu w MSIL lub F # poniżej, które jest warte obejrzenia, i opinię. Usunę tę edycję, jeśli rozwiązanie pojawi się dalej na stronie.
źródło
Odpowiedzi:
Ponieważ
Enum
Type implementujeIConvertible
interfejs, lepsza implementacja powinna wyglądać mniej więcej tak:Nadal pozwoli to na wdrożenie typów wartości
IConvertible
. Szanse są jednak rzadkie.źródło
Ta funkcja jest wreszcie obsługiwana w C # 7.3!
Poniższy fragment kodu (z próbek dotnet ) pokazuje, jak:
Pamiętaj, aby ustawić wersję językową w projekcie C # na wersję 7.3.
Oryginalna odpowiedź poniżej:
Spóźniłem się na grę, ale podjąłem wyzwanie, aby zobaczyć, jak można to zrobić. Nie jest to możliwe w C # (lub VB.NET, ale przewiń w dół do F #), ale jest to możliwe w MSIL. Napisałem tę małą .... rzecz
Która generuje funkcję, która wyglądałaby tak, gdyby był poprawny C #:
Następnie z następującym kodem C #:
Niestety oznacza to, że ta część kodu jest napisana w MSIL zamiast w C #, z jedyną dodatkową korzyścią, którą możesz ograniczyć tą metodą
System.Enum
. Jest to również coś w rodzaju bummera, ponieważ kompiluje się w osobny zestaw. Nie oznacza to jednak, że musisz go wdrożyć w ten sposób.Usuwając linię
.assembly MyThing{}
i wywołując ilasmę w następujący sposób:dostajesz moduł sieci zamiast zestawu.
Niestety, VS2010 (i oczywiście wcześniej) nie obsługuje dodawania odniesień do modułu sieciowego, co oznacza, że będziesz musiał zostawić go w 2 osobnych zestawach podczas debugowania. Jedynym sposobem na dodanie ich jako części zestawu byłoby samodzielne uruchomienie csc.exe za pomocą
/addmodule:{files}
argumentu wiersza poleceń. W skrypcie MSBuild nie byłoby to zbyt bolesne. Oczywiście, jeśli jesteś odważny lub głupi, możesz ręcznie uruchomić csc za każdym razem. Z pewnością komplikuje się, ponieważ wiele zestawów potrzebuje do niego dostępu.Tak więc można to zrobić w .Net. Czy to warte dodatkowego wysiłku? Cóż, chyba pozwolę ci zdecydować o tym.
Rozwiązanie F # jako alternatywa
Dodatkowy kredyt: Okazuje się, że ogólne ograniczenie
enum
jest możliwe w co najmniej jednym innym języku .NET oprócz MSIL: F #.Ten jest łatwiejszy w utrzymaniu, ponieważ jest dobrze znanym językiem z pełną obsługą Visual Studio IDE, ale nadal potrzebujesz osobnego projektu w swoim rozwiązaniu. Jednak naturalnie produkuje znacznie różne IL (kod jest bardzo różny) i opiera się na
FSharp.Core
bibliotece, która, podobnie jak każda inna biblioteka zewnętrzna, musi stać się częścią twojej dystrybucji.Oto, jak możesz go użyć (w zasadzie to samo, co rozwiązanie MSIL) i pokazać, że nie działa poprawnie na strukturach synonimicznych inaczej:
źródło
There's no particularly unusual reason why not; we have lots of other things to do, limited budgets, and this one has never made it past the "wouldn't this be nice?" discussion in the language design team.
T
abySystem.Enum
nie byłby w stanie zrobić wszystkie rzeczy, oT
które ludzie mogą oczekiwać, autorzy C # zorientowali mogą również zakazać go całkowicie. Uważam tę decyzję za niefortunną, ponieważ C # po prostu zignorowała jakiekolwiek specjalne postępowanie zSystem.Enum
ograniczeniami, byłoby możliwe napisanieHasAnyFlags<T>(this T it, T other)
metody rozszerzenia, która była o rząd wielkości szybsza niżEnum.HasFlag(Enum)
i która sprawdzała jej argumenty.C # ≥ 7,3
Począwszy od wersji C # 7.3 (dostępnej w Visual Studio 2017 ≥ 15.7), ten kod jest teraz w pełni poprawny:
C # ≤ 7,2
Możesz mieć wymuszone ograniczenie wyliczania przez prawdziwy kompilator, nadużywając dziedziczenia ograniczeń. Poniższy kod określa jednocześnie ograniczenia a
class
orazstruct
ograniczenia:Stosowanie:
Uwaga: jest to wyraźnie określone w specyfikacji języka C # 5.0:
źródło
EnumClassUtils<System.Enum>
jest wystarczający, aby ograniczyć T do dowolnegoSystem.Enum
typu pochodnego.struct
naParse
czym ogranicza go dalej do rzeczywistego typu wyliczeniowego. WEnum
pewnym momencie musisz ograniczyć się do . Aby to zrobić, twoja klasa musi być zagnieżdżona. Zobacz gist.github.com/MrJul/7da12f5f2d6c69f03d79where TClass : class
zyskuje tu ograniczenie?enum DefinitelyNotAnInt : byte { Realize, That, I, Am, Not, An, Int }
enum AlsoNotAnInt : long { Well, Bummer }
Edytować
Na pytanie doskonale odpowiedział już Julien Lebosquain . Chciałbym również, aby rozszerzyć swoją odpowiedź z
ignoreCase
,defaultValue
i opcjonalne argumenty, podczas dodawaniaTryParse
iParseOrDefault
.Przykłady użycia:
Stary
Moje stare ulepszenia odpowiedzi Vivek za pomocą komentarzy i „nowych” zmian:
TEnum
dla jasności dla użytkownikówTryParse
obsłużyignoreCase
istniejący parametr (wprowadzony w VS2010 / .Net 4)default
wartości ogólnej (wprowadzonej w VS2005 / .Net 2)defaultValue
iignoreCase
w wyniku czego:
źródło
Możesz zdefiniować konstruktora statycznego dla klasy, który sprawdzi, czy typ T jest wyliczeniem i wyrzuci wyjątek, jeśli nie jest. Jest to metoda wspomniana przez Jeffery Richtera w jego książce CLR poprzez C #.
Następnie w metodzie parsowania możesz po prostu użyć Enum.Parse (typeof (T), input, true), aby przekonwertować ciąg znaków na wyliczenie. Ostatni prawdziwy parametr służy do ignorowania wielkości liter na wejściu.
źródło
Enum
T
gdy konstruktor został wykonany. Chociaż jest to o wiele przyjemniejsze niż czekanie na konstruktor instancji.Należy również wziąć pod uwagę, że ponieważ wydanie C # 7.3 przy użyciu ograniczeń Enum jest obsługiwane od razu po wyjęciu z pudełka, bez konieczności dodatkowego sprawdzania i innych rzeczy.
Idąc dalej i biorąc pod uwagę, że zmieniłeś wersję językową swojego projektu na C # 7.3, następujący kod będzie działał idealnie:
Jeśli nie wiesz, jak zmienić wersję językową na C # 7.3, zobacz następujący zrzut ekranu:
EDYCJA 1 - Wymagana wersja Visual Studio i rozważenie ReSharper
Aby program Visual Studio rozpoznał nową składnię, potrzebujesz co najmniej wersji 15.7. Można to znaleźć również w uwagach do wydania Microsoftu, zobacz Visual Studio 2017 15.7 Informacje o wersji . Dzięki @MohamedElshawaf za wskazanie tego ważnego pytania.
Proszę również zauważyć, że w moim przypadku ReSharper 2018.1 w chwili pisania tego EDYCJI nie obsługuje jeszcze C # 7.3. Po aktywacji ReSharper podświetla ograniczenie Enum jako błąd informujący, że nie mogę użyć „System.Array”, „System.Delegate”, „System.Enum”, „System.ValueType”, „obiekt” jako ograniczenia parametru typu . ReSharper sugeruje jako szybką poprawkę na usunięcie ograniczenia „Enum” parametru typu T metody
Jeśli jednak tymczasowo wyłączysz ReSharper w menu Narzędzia -> Opcje -> ReSharper Ultimate -> Ogólne , zobaczysz, że składnia jest w porządku, biorąc pod uwagę, że używasz VS 15.7 lub wyższej i C # 7.3 lub wyższej.
źródło
where T : struct, Enum
, aby uniknąć podaniaSystem.Enum
siebie jako parametru typu.struct, Enum
. Moje uzasadnienie wyjaśniono w odpowiedzi i komentarzach tutaj .Zmodyfikowałem próbkę przez dimarzionist. Ta wersja działa tylko z Enums i nie pozwala strukturom na przejście.
źródło
Próbowałem trochę poprawić kod:
źródło
defaultValue.ToString("D", System.Globalization.NumberFormatInfo.CurrentInfo)
nawet jeśli nie wiesz, jaki to jest rodzaj wyliczenia, tylko że obiekt jest wyliczeniem.IsDefined
zrujnuje wielkość liter. W przeciwieństwie doParse
,IsDefined
nie maignoreCase
argumentu, a MSDN mówi, że pasuje tylko do dokładnej wielkości liter .Mam określony wymóg, w którym wymagałem użycia wyliczenia z tekstem powiązanym z wartością wyliczenia. Na przykład, gdy używam wyliczenia, aby określić typ błędu, wymagane jest opisanie szczegółów błędu.
źródło
Mam nadzieję, że to jest pomocne:
źródło
return (TValue)Enum.Parse(typeof (TValue), value);
przezreturn (TValue)Enum.Parse(typeof (TValue), value, true);
Co ciekawe, najwyraźniej jest to możliwe w innych językach (bezpośrednio Managed C ++, IL).
Cytować:
Kto wie
źródło
To jest moje zdanie na ten temat. W połączeniu z odpowiedziami i MSDN
Źródło MSDN
źródło
TEnum
faktycznie jest typem Enum, aletext
jest pustym ciągiem, wówczas pojawia sięArgumentException
powiedzenie „TEnum musi być typem Enum”, nawet jeśli tak jest.Istniejące odpowiedzi są prawdziwe od C # <= 7.2. Istnieje jednak żądanie funkcji języka C # (powiązane z żądaniem funkcji Corefx ), aby umożliwić:
W momencie pisania tego artykułu funkcja ta jest „w dyskusji” na spotkaniach poświęconych rozwojowi języka.
EDYTOWAĆ
Jak wynika z informacji nawfal , zostało to wprowadzone w C # 7.3 .
źródło
Zawsze mi się podobało (można odpowiednio zmodyfikować):
źródło
Podobało mi się rozwiązanie Christophera Currensa z wykorzystaniem IL, ale dla tych, którzy nie chcą zajmować się trudną sprawą włączenia MSIL do procesu kompilacji, napisałem podobną funkcję w języku C #.
Pamiętaj jednak, że nie możesz używać ogólnych ograniczeń, takich jak
where T : Enum
ponieważ Enum jest typem specjalnym. Dlatego muszę sprawdzić, czy dany typ ogólny jest naprawdę wyliczony.Moja funkcja to:
źródło
Zamknąłem rozwiązanie Vivek w klasę użyteczności, z której można ponownie skorzystać. Pamiętaj, że nadal powinieneś zdefiniować ograniczenia typu „gdzie T: struct, IConvertible” na swoim typie.
źródło
Stworzyłem rozszerzenie Metoda
to get integer value from enum
spójrz na implementację metodyto jest użycie
źródło
Jak stwierdzono w innych odpowiedziach wcześniej; Chociaż nie można tego wyrazić w kodzie źródłowym, można to zrobić na poziomie IL. Odpowiedź Christopher Currens pokazuje, jak IL to robi.
Z Fody s dodatek ExtraConstraints.Fody istnieje bardzo prosty sposób, wraz z gromadzeniem narzędzi, aby to osiągnąć. Po prostu dodaj ich pakiety nuget (
Fody
,ExtraConstraints.Fody
) do swojego projektu i dodaj ograniczenia w następujący sposób (Fragment z Readme ExtraConstraints):a Fody doda niezbędną IL, aby było obecne ograniczenie. Zwróć także uwagę na dodatkową funkcję ograniczania delegatów:
Jeśli chodzi o Enums, możesz również zwrócić uwagę na bardzo interesujący Enums.NET .
źródło
To jest moja realizacja. Zasadniczo możesz ustawić dowolny atrybut i działa.
źródło
Jeśli później możesz użyć rzutowania bezpośredniego, myślę, że możesz użyć
System.Enum
klasy bazowej w swojej metodzie, tam gdzie to konieczne. Trzeba tylko ostrożnie wymienić parametry typu. Zatem implementacja metody wyglądałaby następująco:Następnie możesz użyć go w następujący sposób:
źródło
Enum.ToObject()
dałoby bardziej elastyczny wynik. Do tego można było porównywać ciągi znaków bez rozróżniania wielkości liter, coToLower()
Dla kompletności poniżej przedstawiono rozwiązanie Java. Jestem pewien, że to samo można zrobić w języku C #. Pozwala to uniknąć konieczności określania typu w dowolnym miejscu w kodzie - zamiast tego określasz go w ciągach, które próbujesz przeanalizować.
Problem polega na tym, że nie ma sposobu, aby dowiedzieć się, które wyliczenie może pasować do ciągu - więc odpowiedzią jest rozwiązanie tego problemu.
Zamiast akceptować tylko wartość ciągu, zaakceptuj ciąg, który ma zarówno wyliczenie, jak i wartość w postaci „enumeration.value”. Działający kod znajduje się poniżej - wymaga Java 1.8 lub nowszej. Dzięki temu XML byłby bardziej precyzyjny, ponieważ zobaczyłbyś coś takiego jak color = "Color.red" zamiast po prostu color = "red".
Wywołalibyśmy metodę acceptEnumeratedValue () za pomocą łańcucha zawierającego nazwę wyliczenia nazwa wartości kropki.
Metoda zwraca formalną wyliczoną wartość.
źródło