tipica simbolos roger recibe reales que piratas pirata piece one nombre jolly infantil flag dibujo caribe bandera c# enums

c# - simbolos - que nombre recibe la tipica bandera pirata



Método de extensión genérico para ver si una enumeración contiene una bandera (8)

Básicamente, puede utilizar su método de extensión existente, utilizando el tipo Enum lugar de MyEnum . El problema entonces es que no sabe que las enumeraciones son marcas y no permiten que el operador & , por lo que solo tiene que convertir los valores de la enumeración en números.

public static bool Contains(this Enum keys, Enum flag) { if (keys.GetType() != flag.GetType()) throw new ArgumentException("Type Mismatch"); return (Convert.ToUInt64(keys) & Convert.ToUInt64(flag)) != 0; }

Y una prueba de unidad para una buena medida:

[TestMethod] public void TestContains() { var e1 = MyEnum.One | MyEnum.Two; Assert.IsTrue( e1.Contains(MyEnum.Two) ); var e2 = MyEnum.One | MyEnum.Four; Assert.IsFalse(e2.Contains(MyEnum.Two)); }

Considerando esto:

[Flags] public enum MyEnum { One = 1, Two = 2, Four = 4, Eight = 8 } public static class FlagsHelper { public static bool Contains(this MyEnum keys, MyEnum flag) { return (keys & flag) != 0; } }

¿Es posible escribir una versión genérica de Contains que funcione para cualquier enum y no solo para MyEnum ?

Editar:

Esta sería mi versión después de leer tus respuestas:

public static bool Contains(this Enum keys, Enum flag) { ulong keysVal = Convert.ToUInt64(keys); ulong flagVal = Convert.ToUInt64(flag); return (keysVal & flagVal) == flagVal; }

Me he dado cuenta de que es una mala idea comprobar la forma en que estaba comprobando ( return (keys & flag) != 0; ), ya que el parámetro flag podría ser en realidad varias marcas y lo que debe hacerse es devolver el valor verdadero solo si las keys contienen todos de ellos. Además, no buscaría valores nulos ni me aseguraría de que sean del mismo tipo. Es posible que desee utilizar diferentes tipos.


Basé este método en un montón de búsquedas de SO y Google, y utilizando un reflector para ver qué hizo MS para el método .NET 4 HasFlags.

public static class EnumExt { /// <summary> /// Check to see if a flags enumeration has a specific flag set. /// </summary> /// <param name="variable">Flags enumeration to check</param> /// <param name="value">Flag to check for</param> /// <returns></returns> public static bool HasFlag(this Enum variable, Enum value) { if (variable == null) return false; if (value == null) throw new ArgumentNullException("value"); // Not as good as the .NET 4 version of this function, but should be good enough if (!Enum.IsDefined(variable.GetType(), value)) { throw new ArgumentException(string.Format( "Enumeration type mismatch. The flag is of type ''{0}'', was expecting ''{1}''.", value.GetType(), variable.GetType())); } ulong num = Convert.ToUInt64(value); return ((Convert.ToUInt64(variable) & num) == num); } }

Notas:

  • Esto maneja nulos
  • Hace la comprobación de tipo
  • Se convierte a un ulong y puede manejar cualquier valor de enumeración positivo. Microsoft advierte contra el uso de enumeraciones de banderas negativas de todos modos:

    Tenga cuidado si define un número negativo como una constante enumerada de bandera porque muchas posiciones de bandera podrían establecerse en 1, lo que podría hacer que su código sea confuso y fomentar errores de codificación.


Desafortunadamente no hay una buena manera de hacer un método de extensión como este. Para que esto funcione, debe tener un método genérico que funcione con valores de enum . Desafortunadamente, no hay manera de restringir los argumentos genéricos para ser una enumeración

// Ilegal public static bool Contains<T>(this T value, T flag) where T : enum { ... }

Lo mejor que he encontrado es el siguiente

public static bool HasFlag<T>(this System.Enum e, T flag) { var intValue = (int)(object)e; var intFlag = (int)(object)flag; return (intValue & intFlag) != 0; }

Sin embargo, está limitado de varias maneras.

  • No escriba seguro porque no hay ningún requisito, el valor y la bandera tienen el mismo tipo
  • Se supone que todos los valores de enum se basan en int .
  • Hace que el boxeo ocurra para una simple comprobación de bits
  • Tirará si e es null

Este es mi enfoque. Este es un tipo seguro y no hace boxeo ni desempaquetado. Lanza una excepción si el tipo no es una enumeración. Hay una técnica que puede utilizar si desea convertirla en un método estático público que se escribirá en el de Enum, pero no puede ser un método de extensión. Tampoco hay necesidad de verificar si el valor es nulo, ya que la estructura también bloquea las enumeraciones que admiten nulos. No creo que haya mucho que hacer para mejorar este código, con la excepción de que tal vez lo escriba en F # o C ++ / CLI para que pueda ponerle una restricción de enumeración. La idea es crear una función utilizando árboles de expresión que conviertan la enumeración en larga si no es nada más que una enumeración basada en ulong, o ulong y luego y ellos, produciendo esencialmente: return value & flag == flag

public static class EnumExtensions { #region Public Static Methods  /// <summary> /// Determines whether the specified value has flags. Note this method is up to 60 times faster /// than the one that comes with .NET 4 as it avoids any explict boxing or unboxing. /// </summary> /// <typeparam name="TEnum">The type of the enum.</typeparam> /// <param name="value">The value.</param> /// <param name="flag">The flag.</param> /// <returns> /// <c>true</c> if the specified value has flags; otherwise, <c>false</c>. /// </returns> /// <exception cref="ArgumentException">If TEnum is not an enum.</exception> public static bool HasFlags<TEnum>(this TEnum value, TEnum flag) where TEnum:struct,IComparable,IConvertible,IFormattable { return EnumExtensionsInternal<TEnum>.HasFlagsDelegate(value, flag); } #endregion Public Static Methods  #region Nested Classes  static class EnumExtensionsInternal<TEnum> where TEnum : struct,IComparable, IConvertible, IFormattable { #region Public Static Variables  /// <summary> /// The delegate which determines if a flag is set. /// </summary> public static readonly Func<TEnum, TEnum, bool> HasFlagsDelegate = CreateHasFlagDelegate(); #endregion Public Static Variables  #region Private Static Methods  /// <summary> /// Creates the has flag delegate. /// </summary> /// <returns></returns> private static Func<TEnum, TEnum, bool> CreateHasFlagDelegate() { if(!typeof(TEnum).IsEnum) { throw new ArgumentException(string.Format("{0} is not an Enum", typeof(TEnum)), typeof(EnumExtensionsInternal<>).GetGenericArguments()[0].Name); } ParameterExpression valueExpression = Expression.Parameter(typeof(TEnum)); ParameterExpression flagExpression = Expression.Parameter(typeof(TEnum)); ParameterExpression flagValueVariable = Expression.Variable(Type.GetTypeCode(typeof(TEnum)) == TypeCode.UInt64 ? typeof(ulong) : typeof(long)); Expression<Func<TEnum, TEnum, bool>> lambdaExpression = Expression.Lambda<Func<TEnum, TEnum, bool>>( Expression.Block( new[] { flagValueVariable }, Expression.Assign( flagValueVariable, Expression.Convert( flagExpression, flagValueVariable.Type ) ), Expression.Equal( Expression.And( Expression.Convert( valueExpression, flagValueVariable.Type ), flagValueVariable ), flagValueVariable ) ), valueExpression, flagExpression ); return lambdaExpression.Compile(); } #endregion Private Static Methods  } #endregion Nested Classes  }

Como olvidé que el árbol de expresiones anterior es .NET 4, solo el siguiente método debería funcionar en .NET 3.5 para crear el mismo árbol de expresiones:

private static Func<TEnum, TEnum, bool> CreateHasFlagDelegate2() { if(!typeof(TEnum).IsEnum) { throw new ArgumentException(string.Format("{0} is not an Enum", typeof(TEnum)), typeof(EnumExtensionsInternal<>).GetGenericArguments()[0].Name); } ParameterExpression valueExpression = Expression.Parameter( typeof(TEnum), typeof(TEnum).Name ); ParameterExpression flagExpression = Expression.Parameter( typeof(TEnum), typeof(TEnum).Name ); var targetType = Type.GetTypeCode(typeof(TEnum)) == TypeCode.UInt64 ? typeof(ulong) : typeof(long); Expression<Func<TEnum, TEnum, bool>> lambdaExpression = Expression.Lambda<Func<TEnum, TEnum, bool>>( Expression.Equal( Expression.And( Expression.Convert( valueExpression, targetType ), Expression.Convert( flagExpression, targetType ) ), Expression.Convert( flagExpression, targetType ) ), valueExpression, flagExpression ); return lambdaExpression.Compile(); }

esta versión debería compilarse en .NET 3.5 y, si no, no puedo entender por qué.


Este es un ejemplo de algo que debería funcionar.

public static bool IsValid<T>(this T value) { return Enum.IsDefined(value.GetType(), value); }


No estoy seguro si está utilizando .NET 4.0 o no, pero viene con el método estático Enum.HasFlags() .

- Código eliminado (la solución aceptada ya lo tiene) -


Otra forma de implementar la función HasFlag para .NET Framework 3.5.

public static bool HasFlag(this Enum e, Enum flag) { // Check whether the flag was given if (flag == null) { throw new ArgumentNullException("flag"); } // Compare the types of both enumerations if (e.GetType() != (flag.GetType())) { throw new ArgumentException(string.Format( "The type of the given flag is not of type {0}", e.GetType()), "flag"); } // Get the type code of the enumeration var typeCode = e.GetTypeCode(); // If the underlying type of the flag is signed if (typeCode == TypeCode.SByte || typeCode == TypeCode.Int16 || typeCode == TypeCode.Int32 || typeCode == TypeCode.Int64) { return (Convert.ToInt64(e) & Convert.ToInt64(flag)) != 0; } // If the underlying type of the flag is unsigned if (typeCode == TypeCode.Byte || typeCode == TypeCode.UInt16 || typeCode == TypeCode.UInt32 || typeCode == TypeCode.UInt64) { return (Convert.ToUInt64(e) & Convert.ToUInt64(flag)) != 0; } // Unsupported flag type throw new Exception(string.Format("The comparison of the type {0} is not implemented.", e.GetType().Name)); }

Este método de extensión admite todos los tipos posibles para una enumeración ( byte , sbyte , short , ushort , int , uint , long y ulong ). Básicamente, el método verifica si la enumeración dada está firmada / sin firmar y convierte la bandera al tipo con el tamaño más alto de los tipos admitidos para una enumeración. Luego, se realiza una comparación simple utilizando el operador & .

Como se explica en otras publicaciones, no podemos definir una restricción del tipo genérico con una enumeración y no tiene sentido usar genéricos con una restricción de struct , porque los desarrolladores podrían insertar otros tipos o estructuras de enumeración. Entonces, creo que es mejor no usar un método genérico para eso.


Tengo otro enfoque aquí que simplemente cociné rápidamente usando el hecho de que Delegate.CreateDelegate permite la conversión entre métodos para Enum y sus tipos subyacentes. El siguiente enfoque es muy parecido a mi respuesta anterior, pero creo que podría ser más fácil de leer para las personas que no conocen la sintaxis del árbol de expresiones. Básicamente, sabemos que las Enums solo tienen 8 tipos subyacentes posibles, por lo que simplemente creamos un método estático para cada llamada que pueda usar. Dado que voy por brevedad, utilizo métodos anónimos que se denominaron de la misma forma que los posibles valores de código de tipo. Este enfoque funcionará en .Net 3.5 ::

public static class EnumHelper { delegate bool HasFlag<T>(T left,T right); static readonly HasFlag<Byte> Byte = (x,y)=> (x&y) ==y; static readonly HasFlag<SByte> Sbyte = (x,y)=> (x&y) ==y; static readonly HasFlag<Int16> Int16 = (x,y)=> (x&y) ==y; static readonly HasFlag<UInt16> UInt16 = (x,y)=> (x&y) ==y; static readonly HasFlag<Int32> Int32 = (x,y)=> (x&y) ==y; static readonly HasFlag<UInt32> UInt32 = (x,y)=> (x&y) ==y; static readonly HasFlag<Int64> Int64 = (x,y)=> (x&y) ==y; static readonly HasFlag<UInt64> UInt64 = (x,y)=> (x&y) ==y; public static bool HasFlags<TEnum>(this TEnum @enum,TEnum flag) where TEnum:struct,IConvertible,IComparable,IFormattable { return Enum<TEnum>.HasFlag(@enum,flag); } class Enum<TEnum> where TEnum:struct,IConvertible,IComparable,IFormattable { public static HasFlag<TEnum> HasFlag = CreateDelegate(); static HasFlag<TEnum> CreateDelegate() { if (!typeof(TEnum).IsEnum) throw new ArgumentException(string.Format("{0} is not an enum", typeof(TEnum)), typeof(Enum<>).GetGenericArguments()[0].Name); var delegateName = Type.GetTypeCode(typeof(TEnum)).ToString(); var @delegate = typeof(EnumHelper).GetField(delegateName,BindingFlags.Static | BindingFlags.NonPublic).GetValue(null) as Delegate; return Delegate.CreateDelegate(typeof(HasFlag<TEnum>), @delegate.Method) as HasFlag<TEnum>; } } }