it-swarm.com.de

Häufigste bitweise C # -Operationen für Aufzählungen

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?

196
steffenj

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
285
Hugoware

In .NET 4 können Sie jetzt schreiben:

flags.HasFlag(FlagsEnum.Bit4)
106
Drew Noakes

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);
86
Stephen Deken

@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.

22
Chuck Dee

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.

  1. HasFlag ist nicht typsicher, da es alle Arten von Enum-Wert-Argumenten akzeptiert, nicht nur den angegebenen Enum-Typ.
  2. 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.
  3. 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.

Fahnen kombinieren

.NETZ flags | otherFlags

Enums.NET flags.CombineFlags(otherFlags)


Fahnen entfernen

.NETZ flags & ~otherFlags

Enums.NET flags.RemoveFlags(otherFlags)


Gemeinsame Flaggen

.NETZ flags & otherFlags

Enums.NET flags.CommonFlags(otherFlags)


Flaggen umschalten

.NETZ flags ^ otherFlags

Enums.NET flags.ToggleFlags(otherFlags)


Hat alle Markierungsfahnen

.NETZ (flags & otherFlags) == otherFlags Oder flags.HasFlag(otherFlags)

Enums.NET flags.HasAllFlags(otherFlags)


Hat irgendwelche Flaggen

.NETZ (flags & otherFlags) != 0

Enums.NET flags.HasAnyFlags(otherFlags)


Holen Sie sich Flags

.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 .

9
TylerBrinkley

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))
7
Petesh

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;
3
Nashirak

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];

  }
}
2
Tony Tanzillo

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

0
workmad3

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;
        }
    }
}
0
Mark Bamford