using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using Developpez.Dotnet.Properties; using JetBrains.Annotations; using CH = Developpez.Dotnet.CompatibilityHelper; #if !NETFX_CORE using System.ComponentModel; using Developpez.Dotnet.Collections; #endif namespace Developpez.Dotnet { /// /// Fournit des méthodes d'extension pour manipuler les énumérations /// public static class EnumExtensions { /// /// Cette classe permet de mettre en cache les informations d'un type /// d'énumération en particulier. Les informations ne sont évaluées /// qu'une fois par type, dans le constructeur statique /// /// Le type d'énumération private static class EnumInfoCache #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { // ReSharper disable StaticFieldInGenericType private static readonly bool _isEnum; private static readonly bool _hasFlags; private static readonly Type _underlyingType; private static readonly T[] _values; private static readonly T[] _flagValues; #if !NETFX_CORE private static readonly IDictionary _descriptions; #endif public static readonly Func And; public static readonly Func Or; public static readonly Func Not; // ReSharper restore StaticFieldInGenericType static EnumInfoCache() { #if NETFX_CORE _isEnum = typeof(T).GetTypeInfo().IsEnum; #else _isEnum = typeof(T).IsEnum; #endif if (_isEnum) { _underlyingType = Enum.GetUnderlyingType(typeof(T)); _values = CH.Enum.GetValues(typeof(T)).Cast().ToArray(); _flagValues = _values.Where(x => IsPowerOfTwo(Convert.ToUInt64(x))).ToArray(); #if NETFX_CORE _hasFlags = typeof (T).GetTypeInfo().IsDefined(typeof(FlagsAttribute)); #else _hasFlags = Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)); _descriptions = new DefaultDictionary(false); foreach (var v in _values) { _descriptions.Add(v, GetDescription(v)); } #endif And = CreateFunc(Expression.And); Or = CreateFunc(Expression.Or); Not = CreateFunc(Expression.Not); } } private static Func CreateFunc(Func expressionFactory) { var arg = Expression.Parameter(typeof(T), "x"); var castArg = Expression.Convert(arg, _underlyingType); var body = expressionFactory(castArg); var castBody = Expression.Convert(body, typeof(T)); var expr = Expression.Lambda>(castBody, arg); return expr.Compile(); } private static Func CreateFunc(Func expressionFactory) { var left = Expression.Parameter(typeof(T), "x"); var right = Expression.Parameter(typeof(T), "y"); var castLeft = Expression.Convert(left, _underlyingType); var castRight = Expression.Convert(right, _underlyingType); var body = expressionFactory(castLeft, castRight); var castBody = Expression.Convert(body, typeof(T)); var expr = Expression.Lambda>(castBody, left, right); return expr.Compile(); } public static bool IsEnum { get { return _isEnum; } } public static bool HasFlags { get { return _hasFlags; } } public static IEnumerable Values { get { return _values; } } public static IEnumerable FlagValues { get { return _flagValues; } } #if !NETFX_CORE public static IDictionary Descriptions { get { return _descriptions; } } private static string GetDescription(T value) { Type type = typeof(T); string name = Enum.GetName(type, value); if (name != null) { FieldInfo field = type.GetField(name); if (field != null) { DescriptionAttribute attr = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute; if (attr != null) { return attr.Description; } } } return null; } #endif private static bool IsPowerOfTwo(ulong x) { return (x != 0) && (x & (x - 1)) == 0; } } /// /// Vérifie que le type spécifié est une énumération, et optionnellement s'il a l'attribut 'Flags'. /// Si ce n'est pas le cas, une exception est levée. /// /// indique si le type à vérifier doit porter l'attribut Flags /// Le type à vérifier [AssertionMethod] private static void CheckIsEnum(bool withFlags) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { if (!EnumInfoCache.IsEnum) throw new ArgumentException(string.Format(ExceptionMessages.TypeIsNotEnum, typeof(T).FullName)); if (withFlags && !EnumInfoCache.HasFlags) throw new ArgumentException(string.Format(ExceptionMessages.TypeNotDecoratedWithFlags, typeof(T).FullName)); } /// /// Vérifie si le flag spécifié est présent dans la valeur /// /// Le type de l'énumération /// La combinaison de flags à vérifier /// Le flag à vérifier /// true si le flag est présent, false sinon public static bool HasFlag(this T value, T flag) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { CheckIsEnum(true); return !EnumInfoCache.And(value, flag).Equals(default(T)); } /// /// Convertit une combinaison de flags en une liste de valeurs de l'énumération, en /// excluant les combinaisons prédéfinies. /// /// Le type de l'énumération /// La combinaison de flags à convertir /// La liste des flags présents dans la combinaison public static IEnumerable GetFlags(this T value) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { CheckIsEnum(true); return value.GetFlagsIterator(true); } /// /// Convertit une combinaison de flags en une liste de valeurs de l'énumération, en /// excluant optionnellement les combinaisons prédéfinies. /// /// Le type de l'énumération /// La combinaison de flags à convertir /// Indique si seuls les flags simples sont renvoyés, en excluant les combinaisons connues. /// La liste des flags présents dans la combinaison public static IEnumerable GetFlags(this T value, bool flagsOnly) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { CheckIsEnum(true); return value.GetFlagsIterator(flagsOnly); } private static IEnumerable GetFlagsIterator(this T value, bool flagsOnly) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { var values = flagsOnly ? EnumInfoCache.FlagValues : EnumInfoCache.Values; foreach (T flag in values) { if (value.HasFlag(flag)) yield return flag; } } /// /// Définit la valeur d'un ou des flags dans une combinaison de flags /// /// Le type de l'énumération /// La combinaison de flags à modifier /// Le ou les flags à définir /// true pour ajouter le flag, false pour l'enlever /// La combinaison résultant de l'ajout ou de la suppression des flags public static T SetFlags(this T value, T flags, bool on) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { CheckIsEnum(true); if (on) return EnumInfoCache.Or(value, flags); return EnumInfoCache.And(value, EnumInfoCache.Not(flags)); } /// /// Ajoute un ou des flags à une combinaison /// /// Le type de l'énumération /// La combinaison de flags à modifier /// Le ou les flags à ajouter /// La combinaison résultant de l'ajout des flags public static T SetFlags(this T value, T flags) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { return value.SetFlags(flags, true); } /// /// Supprime un ou des flags d'une combinaison /// /// Le type de l'énumération /// La combinaison de flags à modifier /// Le ou les flags à supprimer /// La combinaison résultant de la suppression des flags public static T ClearFlags(this T value, T flags) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { return value.SetFlags(flags, false); } /// /// Convertit une liste de flags en une combinaison de ces flags /// /// Le type de l'énumération /// La liste de flags à combiner /// La combinaison des flags de la liste public static T CombineFlags(this IEnumerable flags) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { CheckIsEnum(true); T tmp = default(T); foreach (T flag in flags) { tmp = EnumInfoCache.Or(tmp, flag); } return tmp; } #if !NETFX_CORE /// /// Renvoie, si elle est définie, la description d'une valeur d'une énumération, spécifiée par /// l'attribut DescriptionAttribute. Si la description n'est pas définie, renvoie null. /// /// Le type de l'énumération /// la valeur pour laquelle obtenir une description /// La description de la valeur si elle est définie, null sinon. public static string GetDescription(this T value) where T : struct, IComparable, IFormattable, IConvertible { CheckIsEnum(false); return EnumInfoCache.Descriptions[value]; } #endif /// /// Convertit une chaine en une valeur d'énumération, en ignorant éventuellement la casse /// /// Type d'énumération souhaité /// Chaine à convertir /// true pour ignorer la casse, false sinon /// La valeur d'énumération correspondant à la chaine public static T ToEnum(this string stringValue, bool ignoreCase) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { CheckIsEnum(false); return (T)Enum.Parse(typeof(T), stringValue, ignoreCase); } /// /// Convertit une chaine en une valeur d'énumération /// /// Type d'énumération souhaité /// Chaine à convertir /// La valeur d'énumération correspondant à la chaine public static T ToEnum(this string stringValue) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { return stringValue.ToEnum(false); } /// /// Convertit un entier signé sur 8 bits en une valeur d'énumération /// /// Type d'énumération souhaité /// Valeur à convertir /// La valeur d'énumération correspondant à la valeur public static T ToEnum(this sbyte value) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { CheckIsEnum(false); return (T)Enum.ToObject(typeof(T), value); } /// /// Convertit un entier signé sur 16 bits en une valeur d'énumération /// /// Type d'énumération souhaité /// Valeur à convertir /// La valeur d'énumération correspondant à la valeur public static T ToEnum(this short value) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { CheckIsEnum(false); return (T)Enum.ToObject(typeof(T), value); } /// /// Convertit un entier signé sur 32 bits en une valeur d'énumération /// /// Type d'énumération souhaité /// Valeur à convertir /// La valeur d'énumération correspondant à la valeur public static T ToEnum(this int value) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { CheckIsEnum(false); return (T)Enum.ToObject(typeof(T), value); } /// /// Convertit un entier signé sur 64 bits en une valeur d'énumération /// /// Type d'énumération souhaité /// Valeur à convertir /// La valeur d'énumération correspondant à la valeur public static T ToEnum(this long value) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { CheckIsEnum(false); return (T)Enum.ToObject(typeof(T), value); } /// /// Convertit un entier non signé sur 8 bits en une valeur d'énumération /// /// Type d'énumération souhaité /// Valeur à convertir /// La valeur d'énumération correspondant à la valeur public static T ToEnum(this byte value) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { CheckIsEnum(false); return (T)Enum.ToObject(typeof(T), value); } /// /// Convertit un entier non signé sur 16 bits en une valeur d'énumération /// /// Type d'énumération souhaité /// Valeur à convertir /// La valeur d'énumération correspondant à la valeur public static T ToEnum(this ushort value) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { CheckIsEnum(false); return (T)Enum.ToObject(typeof(T), value); } /// /// Convertit un entier non signé sur 32 bits en une valeur d'énumération /// /// Type d'énumération souhaité /// Valeur à convertir /// La valeur d'énumération correspondant à la valeur public static T ToEnum(this uint value) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { CheckIsEnum(false); return (T)Enum.ToObject(typeof(T), value); } /// /// Convertit un entier non signé sur 64 bits en une valeur d'énumération /// /// Type d'énumération souhaité /// Valeur à convertir /// La valeur d'énumération correspondant à la valeur public static T ToEnum(this ulong value) #if NETFX_CORE where T : struct, IComparable, IFormattable #else where T : struct, IComparable, IFormattable, IConvertible #endif { CheckIsEnum(false); return (T)Enum.ToObject(typeof(T), value); } } }