Während meines ganzen Lebens kann ich mich nicht erinnern, wie ich ein Bit in einem Bitfeld setzen, löschen, umschalten oder testen soll. Entweder bin ich mir nicht sicher oder ich verwechsle sie, weil ich sie selten brauche. Also ein "bisschen Spickzettel" wäre schön zu haben.
Beispielsweise:
flags = flags | FlagsEnum.Bit4; // Set bit 4.
oder
if ((flags & FlagsEnum.Bit4)) == FlagsEnum.Bit4) // Is there a less verbose way?
Können Sie Beispiele für alle anderen gebräuchlichen Operationen geben, vorzugsweise in C # -Syntax unter Verwendung einer [Flags] -Aufzählung?
Ich habe einige weitere Arbeiten an diesen Erweiterungen durchgeführt - Den Code finden Sie hier
Ich habe einige Erweiterungsmethoden geschrieben, die System.Enum erweitern, die ich oft verwende ... Ich behaupte nicht, dass sie kugelsicher sind, aber sie haben geholfen ... Kommentare entfernt ...
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);
}
}
}
}
Dann werden sie wie folgt verwendet
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
In .NET 4 können Sie jetzt schreiben:
flags.HasFlag(FlagsEnum.Bit4)
Das Idiom ist, den bitweisen oder gleichen Operator zu verwenden, um Bits zu setzen:
flags |= 0x04;
Um ein wenig zu löschen, ist die Redewendung bitweise und mit Negation zu verwenden:
flags &= ~0x04;
Manchmal haben Sie einen Versatz, der Ihr Bit identifiziert, und dann lautet die Redewendung, diese mit Linksverschiebung zu kombinieren:
flags |= 1 << offset;
flags &= ~(1 << offset);
@Drew
Beachten Sie, dass das Enum.HasFlag im Vergleich zum manuellen Schreiben des Codes mit einer erheblichen Leistungseinbuße verbunden ist, außer in den einfachsten Fällen. Betrachten Sie den folgenden Code:
[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();
}
}
Über 10 Millionen Iterationen dauert die HasFlags-Erweiterungsmethode satte 4793 ms, verglichen mit 27 ms für die standardmäßige bitweise Implementierung.
Die in .NET integrierten Flag-Enum-Operationen sind leider recht begrenzt. Die meiste Zeit bleibt es Benutzern, die bitweise Operationslogik herauszufinden.
In .NET 4 wurde die Methode HasFlag
zu Enum
hinzugefügt, was den Code des Benutzers vereinfacht, aber leider gibt es viele Probleme damit.
HasFlag
ist nicht typsicher, da es alle Arten von Enum-Wert-Argumenten akzeptiert, nicht nur den angegebenen Enum-Typ.HasFlag
ist nicht eindeutig, ob geprüft wird, ob der Wert alle oder einige der vom Argument enum value bereitgestellten Flags enthält. Es ist übrigens alles.HasFlag
ist ziemlich langsam, da es Boxen erfordert, was Zuordnungen und somit mehr Speicherbereinigungen verursacht.Zum Teil aufgrund der eingeschränkten Unterstützung von .NET für Flag-Enums habe ich die OSS-Bibliothek Enums.NET geschrieben, die diese Probleme behebt und den Umgang mit Flag-Enums erheblich vereinfacht.
Im Folgenden finden Sie einige der bereitgestellten Vorgänge sowie die entsprechenden Implementierungen, die nur das .NET-Framework verwenden.
.NETZ flags | otherFlags
Enums.NET flags.CombineFlags(otherFlags)
.NETZ flags & ~otherFlags
Enums.NET flags.RemoveFlags(otherFlags)
.NETZ flags & otherFlags
Enums.NET flags.CommonFlags(otherFlags)
.NETZ flags ^ otherFlags
Enums.NET flags.ToggleFlags(otherFlags)
.NETZ (flags & otherFlags) == otherFlags
Oder flags.HasFlag(otherFlags)
Enums.NET flags.HasAllFlags(otherFlags)
.NETZ (flags & otherFlags) != 0
Enums.NET flags.HasAnyFlags(otherFlags)
.NETZ
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()
Ich versuche, diese Verbesserungen in .NET Core und möglicherweise in .NET Framework zu integrieren. Sie können sich meinen Vorschlag ansehen hier .
C++ - Syntax, vorausgesetzt, Bit 0 ist LSB, vorausgesetzt, Flags haben lange Vorzeichen:
Überprüfen Sie, ob Set:
flags & (1UL << (bit to test# - 1))
Überprüfen Sie, ob nicht festgelegt:
invert test !(flag & (...))
Einstellen:
flag |= (1UL << (bit to set# - 1))
Klar:
flag &= ~(1UL << (bit to clear# - 1))
Umschalten:
flag ^= (1UL << (bit to set# - 1))
Um ein Bit zu testen, gehen Sie wie folgt vor: (Unter der Annahme, dass Flags eine 32-Bit-Zahl sind)
Testbit:
if((flags & 0x08) == 0x08)
flags = flags ^ 0x08;
flags = flags & 0xFFFFFF7F;
Dies wurde inspiriert durch die Verwendung von Sets als Indexer in Delphi, als:
/// 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];
}
}
C++ - Operationen sind: & | ^ ~ (für und oder xoder und nicht bitweise Operationen). Ebenfalls von Interesse sind >> und <<, die Bitverschiebungsoperationen sind.
Um zu testen, ob ein Bit in einem Flag gesetzt ist, verwenden Sie: if (flags & 8) // testet, ob Bit 4 gesetzt ist
Verwenden Sie für die beste Leistung und Null-Müll:
using System;
using T = MyNamespace.MyFlags;
namespace MyNamespace
{
[Flags]
public enum MyFlags
{
None = 0,
Flag1 = 1,
Flag2 = 2
}
static class MyFlagsEx
{
public static bool Has(this T type, T value)
{
return (type & value) == value;
}
public static bool Is(this T type, T value)
{
return type == value;
}
public static T Add(this T type, T value)
{
return type | value;
}
public static T Remove(this T type, T value)
{
return type & ~value;
}
}
}