Chcę napisać funkcję, która może zweryfikować podaną wartość (przekazaną jako ciąg) w porównaniu z możliwymi wartościami pliku enum
. W przypadku dopasowania powinien zwrócić instancję wyliczenia; w przeciwnym razie powinien zwrócić wartość domyślną.
Funkcja nie może wewnętrznie używać try
/ catch
, co wyklucza używanie Enum.Parse
, które zgłasza wyjątek, gdy zostanie podany nieprawidłowy argument.
Chciałbym użyć czegoś w rodzaju TryParse
funkcji, aby zaimplementować to:
public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
object enumValue;
if (!TryParse (typeof (TEnum), strEnumValue, out enumValue))
{
return defaultValue;
}
return (TEnum) enumValue;
}
Odpowiedzi:
Jak powiedzieli inni, musisz wdrożyć własne
TryParse
. Simon Mourier zapewnia pełną implementację, która dba o wszystko.Jeśli używasz wyliczeń pól bitowych (tj. Flag), musisz również obsłużyć łańcuch, taki jak
"MyEnum.Val1|MyEnum.Val2"
będący kombinacją dwóch wartości wyliczeniowych. Jeśli po prostu wywołaszEnum.IsDefined
ten ciąg, zwróci on fałsz, mimo żeEnum.Parse
obsługuje go poprawnie.Aktualizacja
Jak wspomnieli Lisa i Christian w komentarzach,
Enum.TryParse
jest teraz dostępny dla języka C # w .NET4 i nowszych.Dokumenty MSDN
źródło
Enum.IsDefined załatwi sprawę. Może nie być tak wydajna, jak prawdopodobnie TryParse, ale będzie działać bez obsługi wyjątków.
public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue) { if (!Enum.IsDefined(typeof(TEnum), strEnumValue)) return defaultValue; return (TEnum)Enum.Parse(typeof(TEnum), strEnumValue); }
Warto zauważyć:
TryParse
w .NET 4.0 dodano metodę.źródło
GetNames
. Wewnętrznie wszystkie te metody (w tymParse
) używająGetHashEntry
, co powoduje rzeczywistą refleksję - raz. Z drugiej strony .NET 4.0 ma TryParse i jest też ogólny :)Oto niestandardowa implementacja
EnumTryParse
. W przeciwieństwie do innych typowych implementacji obsługuje również wyliczenie oznaczoneFlags
atrybutem./// <summary> /// Converts the string representation of an enum to its Enum equivalent value. A return value indicates whether the operation succeeded. /// This method does not rely on Enum.Parse and therefore will never raise any first or second chance exception. /// </summary> /// <param name="type">The enum target type. May not be null.</param> /// <param name="input">The input text. May be null.</param> /// <param name="value">When this method returns, contains Enum equivalent value to the enum contained in input, if the conversion succeeded.</param> /// <returns> /// true if s was converted successfully; otherwise, false. /// </returns> public static bool EnumTryParse(Type type, string input, out object value) { if (type == null) throw new ArgumentNullException("type"); if (!type.IsEnum) throw new ArgumentException(null, "type"); if (input == null) { value = Activator.CreateInstance(type); return false; } input = input.Trim(); if (input.Length == 0) { value = Activator.CreateInstance(type); return false; } string[] names = Enum.GetNames(type); if (names.Length == 0) { value = Activator.CreateInstance(type); return false; } Type underlyingType = Enum.GetUnderlyingType(type); Array values = Enum.GetValues(type); // some enums like System.CodeDom.MemberAttributes *are* flags but are not declared with Flags... if ((!type.IsDefined(typeof(FlagsAttribute), true)) && (input.IndexOfAny(_enumSeperators) < 0)) return EnumToObject(type, underlyingType, names, values, input, out value); // multi value enum string[] tokens = input.Split(_enumSeperators, StringSplitOptions.RemoveEmptyEntries); if (tokens.Length == 0) { value = Activator.CreateInstance(type); return false; } ulong ul = 0; foreach (string tok in tokens) { string token = tok.Trim(); // NOTE: we don't consider empty tokens as errors if (token.Length == 0) continue; object tokenValue; if (!EnumToObject(type, underlyingType, names, values, token, out tokenValue)) { value = Activator.CreateInstance(type); return false; } ulong tokenUl; switch (Convert.GetTypeCode(tokenValue)) { case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: case TypeCode.SByte: tokenUl = (ulong)Convert.ToInt64(tokenValue, CultureInfo.InvariantCulture); break; //case TypeCode.Byte: //case TypeCode.UInt16: //case TypeCode.UInt32: //case TypeCode.UInt64: default: tokenUl = Convert.ToUInt64(tokenValue, CultureInfo.InvariantCulture); break; } ul |= tokenUl; } value = Enum.ToObject(type, ul); return true; } private static char[] _enumSeperators = new char[] { ',', ';', '+', '|', ' ' }; private static object EnumToObject(Type underlyingType, string input) { if (underlyingType == typeof(int)) { int s; if (int.TryParse(input, out s)) return s; } if (underlyingType == typeof(uint)) { uint s; if (uint.TryParse(input, out s)) return s; } if (underlyingType == typeof(ulong)) { ulong s; if (ulong.TryParse(input, out s)) return s; } if (underlyingType == typeof(long)) { long s; if (long.TryParse(input, out s)) return s; } if (underlyingType == typeof(short)) { short s; if (short.TryParse(input, out s)) return s; } if (underlyingType == typeof(ushort)) { ushort s; if (ushort.TryParse(input, out s)) return s; } if (underlyingType == typeof(byte)) { byte s; if (byte.TryParse(input, out s)) return s; } if (underlyingType == typeof(sbyte)) { sbyte s; if (sbyte.TryParse(input, out s)) return s; } return null; } private static bool EnumToObject(Type type, Type underlyingType, string[] names, Array values, string input, out object value) { for (int i = 0; i < names.Length; i++) { if (string.Compare(names[i], input, StringComparison.OrdinalIgnoreCase) == 0) { value = values.GetValue(i); return true; } } if ((char.IsDigit(input[0]) || (input[0] == '-')) || (input[0] == '+')) { object obj = EnumToObject(underlyingType, input); if (obj == null) { value = Activator.CreateInstance(type); return false; } value = obj; return true; } value = Activator.CreateInstance(type); return false; }
źródło
Activator.CreateInstance(type)
do tworzenia domyślnej wartości wyliczenia, a nieEnum.ToObject(type, 0)
. Tylko kwestia gustu?W końcu musisz to zaimplementować wokół
Enum.GetNames
:public bool TryParseEnum<T>(string str, bool caseSensitive, out T value) where T : struct { // Can't make this a type constraint... if (!typeof(T).IsEnum) { throw new ArgumentException("Type parameter must be an enum"); } var names = Enum.GetNames(typeof(T)); value = (Enum.GetValues(typeof(T)) as T[])[0]; // For want of a better default foreach (var name in names) { if (String.Equals(name, str, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)) { value = (T)Enum.Parse(typeof(T), name); return true; } } return false; }
Dodatkowe uwagi:
Enum.TryParse
jest zawarty w .NET 4. Zobacz tutaj http://msdn.microsoft.com/library/dd991876(VS.100).aspxEnum.Parse
wychwytywania wyjątku zgłaszanego, gdy się nie powiedzie. Może to być szybsze po znalezieniu dopasowania, ale prawdopodobnie będzie wolniejsze, jeśli nie. W zależności od danych, które przetwarzasz, może to oznaczać poprawę netto lub nie.EDYCJA: Właśnie widziałem lepszą implementację tego, która buforuje niezbędne informacje: http://damieng.com/blog/2010/10/17/enums-better-syntax-improved-performance-and-tryparse-in-net- 3-5
źródło
Flags
atrybutem.Oparty na .NET 4.5
Przykładowy kod poniżej
using System; enum Importance { None, Low, Medium, Critical } class Program { static void Main() { // The input value. string value = "Medium"; // An unitialized variable. Importance importance; // Call Enum.TryParse method. if (Enum.TryParse(value, out importance)) { // We now have an enum type. Console.WriteLine(importance == Importance.Medium); } } }
Źródła: http://www.dotnetperls.com/enum-parse
źródło
Mam zoptymalizowaną implementację, której możesz użyć w UnconstrainedMelody . W rzeczywistości to po prostu buforowanie listy nazwisk, ale robi to w ładny, mocno wpisany, ogólnie ograniczony sposób :)
źródło
enum EnumStatus { NAO_INFORMADO = 0, ENCONTRADO = 1, BLOQUEADA_PELO_ENTREGADOR = 2, DISPOSITIVO_DESABILITADO = 3, ERRO_INTERNO = 4, AGARDANDO = 5 }
...
if (Enum.TryParse<EnumStatus>(item.status, out status)) { }
źródło
Obecnie nie ma po wyjęciu z pudełka Enum.TryParse. Poproszono o to w Connect ( nadal nie ma Enum.TryParse ) i otrzymano odpowiedź wskazującą możliwe włączenie do następnej platformy po .NET 3.5. Na razie musisz wdrożyć sugerowane obejścia.
źródło
Jedynym sposobem uniknięcia obsługi wyjątków jest użycie metody GetNames (), a wszyscy wiemy, że nie należy nadużywać wyjątków w przypadku typowej logiki aplikacji :)
źródło
Czy buforowanie dynamicznie generowanej funkcji / słownika jest dozwolone?
Ponieważ nie znasz (wydaje się), że typ wyliczenia z wyprzedzeniem, pierwsze wykonanie może wygenerować coś, z czego mogłyby skorzystać kolejne wykonania.
Możesz nawet buforować wynik Enum.GetNames ()
Czy próbujesz zoptymalizować pod kątem procesora lub pamięci? Czy naprawdę potrzebujesz?
źródło
Jak już powiedzieli inni, jeśli nie używasz Try & Catch, musisz użyć IsDefined lub GetNames ... Oto kilka przykładów ... w zasadzie wszystkie są takie same, pierwsza obsługuje wyliczenia dopuszczające wartość null. Wolę drugi, ponieważ jest to rozszerzenie na ciągach, a nie wyliczenia ... ale możesz je mieszać, jak chcesz!
źródło
Nie ma TryParse, ponieważ typ Enum nie jest znany do czasu uruchomienia. TryParse, który jest zgodny z tą samą metodologią, co metoda Date.TryParse, spowoduje zgłoszenie niejawnego błędu konwersji w parametrze ByRef.
Proponuję zrobić coś takiego:
//1 line call to get value MyEnums enumValue = (Sections)EnumValue(typeof(Sections), myEnumTextValue, MyEnums.SomeEnumDefault); //Put this somewhere where you can reuse public static object EnumValue(System.Type enumType, string value, object NotDefinedReplacement) { if (Enum.IsDefined(enumType, value)) { return Enum.Parse(enumType, value); } else { return Enum.Parse(enumType, NotDefinedReplacement); } }
źródło
Try
metod, których wyniki mogą być typami wartości lub którenull
mogą być prawidłowym wynikiem (np.Dictionary.TryGetValue, which has both such traits), the normal pattern is for a
Metoda Try`, aby zwrócićbool
i przekazać wynik jakoout
parametr. Dla tych, które zwracają typy klas, w którychnull
nie jest prawidłowym wynikiem, nie ma trudności z użyciemnull
zwrotu wskazać awarięPrzyjrzyj się samej klasie Enum (struct?). Jest na tym metoda Parse, ale nie jestem pewien co do tryparse.
źródło
Ta metoda skonwertuje typ wyliczenia:
public static TEnum ToEnum<TEnum>(object EnumValue, TEnum defaultValue) { if (!Enum.IsDefined(typeof(TEnum), EnumValue)) { Type enumType = Enum.GetUnderlyingType(typeof(TEnum)); if ( EnumValue.GetType() == enumType ) { string name = Enum.GetName(typeof(HLink.ViewModels.ClaimHeaderViewModel.ClaimStatus), EnumValue); if( name != null) return (TEnum)Enum.Parse(typeof(TEnum), name); return defaultValue; } } return (TEnum)Enum.Parse(typeof(TEnum), EnumValue.ToString()); }
Sprawdza typ bazowy i pobiera nazwę w celu przeanalizowania. Jeśli wszystko zawiedzie, zwróci wartość domyślną.
źródło