Przez całe życie nie pamiętam, jak ustawić, usunąć, przełączyć lub przetestować trochę na polu bitowym. Albo nie jestem pewien, albo je mieszam, ponieważ rzadko ich potrzebuję. Tak więc byłoby miło mieć „ściągawkę”.
Na przykład:
flags = flags | FlagsEnum.Bit4; // Set bit 4.
lub
if ((flags & FlagsEnum.Bit4)) == FlagsEnum.Bit4) // Is there a less verbose way?
Czy możesz podać przykłady wszystkich innych typowych operacji, najlepiej w składni C # przy użyciu wyliczenia [Flagi]?
Odpowiedzi:
Popracowałem trochę nad tymi rozszerzeniami - kod można znaleźć tutaj
Napisałem kilka metod rozszerzenia, które rozszerzają System.Enum, których często używam ... Nie twierdzę, że są kuloodporne, ale pomogły ... Komentarze zostały usunięte ...
Następnie są używane w następujący sposób
źródło
HasFlag
metodaEnum
wymaga boksu.W .NET 4 możesz teraz pisać:
źródło
FlagsEnum
to brzydkie imię. :)[Flags]
teksty stałe powinny mieć nazwy w liczbie mnogiej, więc nazwaFlagsEnum
jest jeszcze bardziej poważne problemy niż brzydotę.Idiom polega na użyciu operatora bitowego lub równego do ustawienia bitów:
Aby trochę wyczyścić, idiomem jest użycie bitowe i z negacją:
Czasami masz przesunięcie, które identyfikuje twój bit, a następnie idiomem jest użycie ich w połączeniu z przesunięciem w lewo:
źródło
@Rysował
Zauważ, że z wyjątkiem najprostszych przypadków Enum.HasFlag niesie ze sobą wysoką wydajność w porównaniu do ręcznego pisania kodu. Rozważ następujący kod:
Ponad 10 milionów iteracji metoda rozszerzenia HasFlags zajmuje aż 4793 ms, w porównaniu do 27 ms dla standardowej implementacji bitowej.
źródło
HasFlag
Metoda polega boks / unboxing, co stanowi dla tej różnicy. Ale koszt jest tak trywialny (0,4 µs), że jeśli nie będziesz w ścisłej pętli, skorzystam z bardziej czytelnego (i mniej prawdopodobnego błędnego) deklaratywnego wywołania API każdego dnia.Wbudowane operacje wyliczania flag .NET są niestety dość ograniczone. Przez większość czasu użytkownicy nie są w stanie zrozumieć logiki operacji bitowej.
W .NET 4
HasFlag
dodano metodę,Enum
która pomaga uprościć kod użytkownika, ale niestety jest z nim wiele problemów.HasFlag
nie jest bezpieczny dla typu, ponieważ przyjmuje dowolny typ argumentu wartości wyliczeniowej, nie tylko dany typ wyliczenia.HasFlag
jest niejednoznaczny, czy sprawdza, czy wartość ma wszystkie czy jakiekolwiek flagi dostarczone przez argument wartości wyliczeniowej. Nawiasem mówiąc, to wszystko.HasFlag
jest raczej powolny, ponieważ wymaga boksu, który powoduje przydziały, a tym samym więcej śmieci.Po części ze względu na ograniczone wsparcie .NET dla wyliczeń flag, napisałem bibliotekę OSS Enums.NET, która rozwiązuje każdy z tych problemów i znacznie ułatwia radzenie sobie z wyliczeniami flag.
Poniżej znajdują się niektóre operacje, które udostępnia wraz z ich równoważnymi implementacjami przy użyciu samego środowiska .NET.
Połącz flagi
.NETTO
flags | otherFlags
Enums.NET
flags.CombineFlags(otherFlags)
Usuń flagi
.NETTO
flags & ~otherFlags
Enums.NET
flags.RemoveFlags(otherFlags)
Wspólne flagi
.NETTO
flags & otherFlags
Enums.NET
flags.CommonFlags(otherFlags)
Przełącz flagi
.NETTO
flags ^ otherFlags
Enums.NET
flags.ToggleFlags(otherFlags)
Ma wszystkie flagi
.NET
(flags & otherFlags) == otherFlags
lubflags.HasFlag(otherFlags)
Enums.NET
flags.HasAllFlags(otherFlags)
Ma dowolne flagi
.NETTO
(flags & otherFlags) != 0
Enums.NET
flags.HasAnyFlags(otherFlags)
Zdobądź flagi
.NETTO
Enums.NET
flags.GetFlags()
Usiłuję włączyć te ulepszenia do .NET Core i być może pełnej wersji .NET Framework. Możesz sprawdzić moją propozycję tutaj .
źródło
Składnia C ++, przy założeniu, że bit 0 to LSB, przy założeniu, że flagi są bez znaku długie:
Sprawdź, czy zestaw:
Sprawdź, jeśli nie ustawiono:
Zestaw:
Jasny:
Przełącznik:
źródło
Aby uzyskać najlepszą wydajność i zero śmieci, użyj tego:
źródło
Aby przetestować bit, wykonaj następujące czynności: (zakładając, że flagi są liczbą 32-bitową)
Bit testowy:
(Jeśli bit 4 jest ustawiony, to jego prawda) Przełącz wstecz (1 - 0 lub 0 - 1): Zresetuj Bit 4 do Zera:źródło
~0x08
zamiast0xFFFFFFF7
... (rzeczywista maska dla 0x8)Zostało to zainspirowane użyciem Setów jako indeksatorów w Delphi, gdy:
źródło
Operacje w C ++ to: & | ^ ~ (dla i, lub, xor i nie bitowych). Interesujące są również >> i <<, które są operacjami przesunięcia bitów.
Tak więc, aby sprawdzić, czy bit jest ustawiony w fladze, użyłbyś: if (flagi i 8) // testy bit 4 został ustawiony
źródło