define - return enum c#
Las operaciones bitwise más comunes de C#en enumeraciones (9)
Por mi vida, no recuerdo cómo configurar, eliminar, alternar o probar un poco en un campo de bits. O no estoy seguro o los mezclo porque rara vez los necesito. Así que sería bueno tener un "bit-cheat-sheet".
Por ejemplo:
flags = flags | FlagsEnum.Bit4; // Set bit 4.
o
if ((flags & FlagsEnum.Bit4)) == FlagsEnum.Bit4) // Is there a less verbose way?
¿Puede dar ejemplos de todas las demás operaciones comunes, preferiblemente en la sintaxis de C # utilizando una enumeración [Flags]?
@Dibujó
Tenga en cuenta que, excepto en los casos más simples, Enum.HasFlag conlleva una gran penalización en el rendimiento en comparación con la escritura manual del código. Considere el siguiente código:
[Flags]
public enum TestFlags
{
One = 1,
Two = 2,
Three = 4,
Four = 8,
Five = 16,
Six = 32,
Seven = 64,
Eight = 128,
Nine = 256,
Ten = 512
}
class Program
{
static void Main(string[] args)
{
TestFlags f = TestFlags.Five; /* or any other enum */
bool result = false;
Stopwatch s = Stopwatch.StartNew();
for (int i = 0; i < 10000000; i++)
{
result |= f.HasFlag(TestFlags.Three);
}
s.Stop();
Console.WriteLine(s.ElapsedMilliseconds); // *4793 ms*
s.Restart();
for (int i = 0; i < 10000000; i++)
{
result |= (f & TestFlags.Three) != 0;
}
s.Stop();
Console.WriteLine(s.ElapsedMilliseconds); // *27 ms*
Console.ReadLine();
}
}
En más de 10 millones de iteraciones, el método de extensión HasFlags toma la friolera de 4793 ms, en comparación con los 27 ms para la implementación bitwise estándar.
El idioma es usar el operador bit a bit o igual para establecer bits:
flags |= 0x04;
Para borrar un poco, el idioma es usar bitwise y con negación:
flags &= ~0x04;
A veces tienes un desplazamiento que identifica tu bit, y luego el idioma es usar estos combinados con el desplazamiento a la izquierda:
flags |= 1 << offset;
flags &= ~(1 << offset);
En .NET 4 ahora puedes escribir:
flags.HasFlag(FlagsEnum.Bit4)
Esto se inspiró en el uso de Sets como indexadores en Delphi, hace mucho tiempo cuando:
/// Example of using a Boolean indexed property
/// to manipulate a [Flags] enum:
public class BindingFlagsIndexer
{
BindingFlags flags = BindingFlags.Default;
public BindingFlagsIndexer()
{
}
public BindingFlagsIndexer( BindingFlags value )
{
this.flags = value;
}
public bool this[BindingFlags index]
{
get
{
return (this.flags & index) == index;
}
set( bool value )
{
if( value )
this.flags |= index;
else
this.flags &= ~index;
}
}
public BindingFlags Value
{
get
{
return flags;
}
set( BindingFlags value )
{
this.flags = value;
}
}
public static implicit operator BindingFlags( BindingFlagsIndexer src )
{
return src != null ? src.Value : BindingFlags.Default;
}
public static implicit operator BindingFlagsIndexer( BindingFlags src )
{
return new BindingFlagsIndexer( src );
}
}
public static class Class1
{
public static void Example()
{
BindingFlagsIndexer myFlags = new BindingFlagsIndexer();
// Sets the flag(s) passed as the indexer:
myFlags[BindingFlags.ExactBinding] = true;
// Indexer can specify multiple flags at once:
myFlags[BindingFlags.Instance | BindingFlags.Static] = true;
// Get boolean indicating if specified flag(s) are set:
bool flatten = myFlags[BindingFlags.FlattenHierarchy];
// use | to test if multiple flags are set:
bool isProtected = ! myFlags[BindingFlags.Public | BindingFlags.NonPublic];
}
}
Las operaciones de C ++ son: & | ^ ~ (para y, o, operaciones xor y no bitwise). También de interés son >> y <<, que son operaciones de cambio de bits.
Por lo tanto, para probar si un bit se establece en un indicador, debe usar: if (flags & 8) // tests bit 4 se ha establecido
Las operaciones de enumeración de bandera incorporadas de .NET son, por desgracia, bastante limitadas. La mayoría de las veces, los usuarios se quedan con la lógica de operación a nivel de bits.
En .NET 4, el método HasFlag
se agregó a Enum
que ayuda a simplificar el código del usuario, pero desafortunadamente hay muchos problemas con él.
-
HasFlag
no es de tipo seguro ya que acepta cualquier tipo de argumento de valor de enumeración, no solo el tipo de enumeración dado. -
HasFlag
es ambiguo en cuanto a si comprueba si el valor tiene todos o alguno de los indicadores proporcionados por el argumento del valor de enumeración. Es todo por cierto. -
HasFlag
es bastante lento, ya que requiere boxeo que causa asignaciones y, por lo tanto, más recolecciones de basura.
Debido en parte al soporte limitado de .NET para las enumeraciones de indicadores, escribí la biblioteca OSS Enums.NET que aborda cada uno de estos problemas y hace que el manejo de enumeraciones de indicadores sea mucho más fácil.
A continuación, se detallan algunas de las operaciones que proporciona junto con sus implementaciones equivalentes utilizando solo el marco .NET.
Combinar banderas
flags | otherFlags
.NET flags | otherFlags
flags | otherFlags
Enums.NET flags.CombineFlags(otherFlags)
Quitar banderas
flags & ~otherFlags
.NET flags & ~otherFlags
Enums.NET flags.RemoveFlags(otherFlags)
Banderas comunes
flags & otherFlags
.NET flags & otherFlags
Enums.NET flags.CommonFlags(otherFlags)
Banderas de palanca
.NET flags ^ otherFlags
Enums.NET flags.ToggleFlags(otherFlags)
Tiene todas las banderas
.NET (flags & otherFlags) == otherFlags
o flags.HasFlag(otherFlags)
Enums.NET flags.HasAllFlags(otherFlags)
Tiene alguna bandera
.NET (flags & otherFlags) != 0
Enums.NET flags.HasAnyFlags(otherFlags)
Obtener banderas
.RED
Enumerable.Range(0, 64)
.Where(bit => ((flags.GetTypeCode() == TypeCode.UInt64 ? (long)(ulong)flags : Convert.ToInt64(flags)) & (1L << bit)) != 0)
.Select(bit => Enum.ToObject(flags.GetType(), 1L << bit))`
Enums.NET flags.GetFlags()
Estoy tratando de incorporar estas mejoras en .NET Core y, eventualmente, en todo el .NET Framework completo. Puedes ver mi propuesta here .
Para probar un bit, haría lo siguiente: (asumiendo que las banderas son un número de 32 bits)
Bit de prueba:
if((flags & 0x08) == 0x08)
(Si se establece el bit 4, entonces es verdadero) Alternar atrás (1 - 0 o 0 - 1):
flags = flags ^ 0x08;
Restablecer Bit 4 a cero:
flags = flags & 0xFFFFFF7F;
Sintaxis de C ++, asumiendo que el bit 0 es LSB, asumiendo que las banderas no tienen signo largo:
Compruebe si está configurado:
flags & (1UL << (bit to test# - 1))
Compruebe si no está configurado:
invert test !(flag & (...))
Conjunto:
flag |= (1UL << (bit to set# - 1))
Claro:
flag &= ~(1UL << (bit to clear# - 1))
Palanca:
flag ^= (1UL << (bit to set# - 1))
Hice un poco más de trabajo en estas extensiones. Puedes encontrar el código aquí.
Escribí algunos métodos de extensión que extienden System.Enum que utilizo a menudo ... No digo que sean a prueba de balas, pero han ayudado ... Comentarios eliminados ...
namespace Enum.Extensions {
public static class EnumerationExtensions {
public static bool Has<T>(this System.Enum type, T value) {
try {
return (((int)(object)type & (int)(object)value) == (int)(object)value);
}
catch {
return false;
}
}
public static bool Is<T>(this System.Enum type, T value) {
try {
return (int)(object)type == (int)(object)value;
}
catch {
return false;
}
}
public static T Add<T>(this System.Enum type, T value) {
try {
return (T)(object)(((int)(object)type | (int)(object)value));
}
catch(Exception ex) {
throw new ArgumentException(
string.Format(
"Could not append value from enumerated type ''{0}''.",
typeof(T).Name
), ex);
}
}
public static T Remove<T>(this System.Enum type, T value) {
try {
return (T)(object)(((int)(object)type & ~(int)(object)value));
}
catch (Exception ex) {
throw new ArgumentException(
string.Format(
"Could not remove value from enumerated type ''{0}''.",
typeof(T).Name
), ex);
}
}
}
}
Luego se usan como las siguientes
SomeType value = SomeType.Grapes;
bool isGrapes = value.Is(SomeType.Grapes); //true
bool hasGrapes = value.Has(SomeType.Grapes); //true
value = value.Add(SomeType.Oranges);
value = value.Add(SomeType.Apples);
value = value.Remove(SomeType.Grapes);
bool hasOranges = value.Has(SomeType.Oranges); //true
bool isApples = value.Is(SomeType.Apples); //false
bool hasGrapes = value.Has(SomeType.Grapes); //false