Voici une mise en œuvre personnalisée de EnumTryParse
. Contrairement à d'autres implémentations courantes, il prend également en charge les enum marqués par l'attribut Flags
attribut.
/// <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;
}
8 votes
Je ne comprends pas cette question ; vous dites "Je veux résoudre ce problème, mais je ne veux utiliser aucune des méthodes qui me donneraient une solution." Quel est l'intérêt ?
1 votes
Quelle est votre aversion pour la solution try/catch ? Si vous essayez d'éviter les exceptions parce qu'elles sont "coûteuses", faites-vous plaisir. Dans 99% des cas, le coût de lancer/capturer une exception est négligeable par rapport à votre code principal.
1 votes
Le coût du traitement des exceptions n'est pas si élevé. Les implémentations internes de toute cette conversion d'énumération sont pleines de gestion d'exceptions. Je n'aime vraiment pas que les exceptions soient lancées et rattrapées pendant la logique normale de l'application. Il peut parfois être utile de casser toutes les exceptions qui sont lancées (même lorsqu'elles sont attrapées). Lancer des exceptions partout rendra l'utilisation beaucoup plus ennuyeuse :)
4 votes
@Domenic : Je cherche simplement une meilleure solution que ce que je connais déjà. Vous n'iriez jamais demander à un service de renseignements ferroviaires un itinéraire ou un train que vous connaissez déjà :) .
2 votes
@Yogi, @Thorarin : essayez... la capture sera toujours mon dernier choix. En ce qui concerne le coût, on ne sait jamais. Que se passe-t-il si quelqu'un appelle ma méthode utilitaire sur une liste de centaines d'éléments ?
2 votes
@Amby, le coût de la simple entrée dans un bloc try/catch est négligeable. Le coût de lancer une exception ne l'est pas, mais c'est censé être exceptionnel, non ? De plus, ne dites pas "on ne sait jamais"... profilez le code et découvrez-le. Ne perdez pas votre temps à vous demander si quelque chose est lent, TROUVEZ-LE !
0 votes
Solution de Darin Dimitrov : stackoverflow.com/a/17400648/3762855