Hayatım boyunca, bir bit alanında bir bitin nasıl ayarlanacağını, silineceğini, değiştirileceğini veya test edileceğini hatırlayamıyorum. Ya emin değilim ya da bunları karıştırıyorum çünkü bunlara nadiren ihtiyacım var. Yani bir "bit-hile-levha" olması güzel olurdu.
Örneğin:
flags = flags | FlagsEnum.Bit4; // Set bit 4.
veya
if ((flags & FlagsEnum.Bit4)) == FlagsEnum.Bit4) // Is there a less verbose way?
Diğer tüm işlemlerden örnekler verebilir misiniz, tercihen bir [Flags] enum kullanarak C # sözdiziminde?
Bu uzantılar üzerinde biraz daha çalıştım - Kodu burada bulabilirsiniz
Sistemimi genişleten bazı uzatma yöntemleri yazdım. Sık kullanıyorum ... Kurşun geçirmez olduklarını iddia etmiyorum, ama yardımcı oldular ... Yorum kaldırıldı ...
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);
}
}
}
}
Sonra aşağıdaki gibi kullanılırlar
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
.NET 4'te şimdi yazabilirsiniz:
flags.HasFlag(FlagsEnum.Bit4)
Deyim, bit ayarlamak için bitsel veya eşit operatörü kullanmaktır:
flags |= 0x04;
Bir parçayı silmek için, deyim bitsel ve olumsuz kullanmaktır:
flags &= ~0x04;
Bazen bitinizi tanımlayan bir ofsetiniz olur ve sonra deyim bunları sola kaydırma ile birlikte kullanmaktır:
flags |= 1 << offset;
flags &= ~(1 << offset);
@Çizdi
En basit durumda, Enum.HasFlag'ın kodu manuel olarak yazmaya kıyasla ağır bir performans cezası taşıdığına dikkat edin. Aşağıdaki kodu göz önünde bulundurun:
[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();
}
}
10 milyonun üzerinde yinelemede, HasFlags uzatma yöntemi standart bit yönünde uygulama için 27 ms ile karşılaştırıldığında, bir miktar 4793 ms alır.
.NET'in yerleşik bayrak enum işlemleri ne yazık ki oldukça sınırlıdır. Kullanıcıların çoğu, bitsel işlem mantığını bulmak için ayrılırlar.
.NET 4'te, HasFlag
yöntemi Enum
öğesine eklenmiştir, bu kullanıcı kodunu basitleştirmeye yardımcı olur, ancak ne yazık ki bununla ilgili birçok sorun var.
HasFlag
, yalnızca verilen enum türünü değil, herhangi bir enum değer bağımsız değişkenini kabul ettiği için tür güvenli değildir.HasFlag
, değerin, enum değer argümanı tarafından sağlanan bayrakların tümüne veya herhangi birine sahip olup olmadığını kontrol edip etmediği konusunda belirsizdir. Hepsi bu arada.HasFlag
, tahsislere ve dolayısıyla daha fazla çöp toplanmasına yol açan boks gerektirdiğinden oldukça yavaştır..NET'in bayrak numaraları için sınırlı desteğinden dolayı, bu sorunların her birini ele alan ve bayrak numaraları ile uğraşmayı çok daha kolaylaştıran OSS kitaplığını Enums.NET yazdım.
Aşağıda sadece .NET çerçevesini kullanarak eşdeğer uygulamaları ile birlikte sağladığı işlemlerden bazıları verilmiştir.
.AĞ flags | otherFlags
Enums.NET flags.CombineFlags(otherFlags)
.AĞ flags & ~otherFlags
Enums.NET flags.RemoveFlags(otherFlags)
.AĞ flags & otherFlags
Enums.NET flags.CommonFlags(otherFlags)
.AĞ flags ^ otherFlags
Enums.NET flags.ToggleFlags(otherFlags)
.AĞ (flags & otherFlags) == otherFlags
veya flags.HasFlag(otherFlags)
Enums.NET flags.HasAllFlags(otherFlags)
.AĞ (flags & otherFlags) != 0
Enums.NET flags.HasAnyFlags(otherFlags)
.AĞ
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()
Bu geliştirmeleri .NET Core'a ve belki de tam .NET Framework'e dahil etmeye çalışıyorum. Teklifimi kontrol edebilirsiniz burada .
C++ sözdizimi, 0 bitinin LSB olduğunu varsayar, bayrakların uzun süre işaretsiz olduğunu varsayarsak:
Set olup olmadığını kontrol edin:
flags & (1UL << (bit to test# - 1))
Ayarlanmadı mı kontrol et:
invert test !(flag & (...))
Ayarlamak:
flag |= (1UL << (bit to set# - 1))
Açık:
flag &= ~(1UL << (bit to clear# - 1))
Geçiş yapma:
flag ^= (1UL << (bit to set# - 1))
Bir parçayı test etmek için aşağıdakileri yaparsınız: (bayrakların 32 bitlik bir sayı olduğunu varsayarak)
Test biti:
if((flags & 0x08) == 0x08)
flags = flags ^ 0x08;
flags = flags & 0xFFFFFF7F;
Delphi'deki Sets dizinleri olarak kullanıldığında esinlenildi, geriye giderken:
/// 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++ işlemleri şunlardır: & | ^ ~ (ve, veya xor ve bitwise işlemleri için). Ayrıca ilgilenilenler >> ve <<, uç değiştirme işlemleri.
Böylece, bir bayrakta bir bitin test edilmesini test etmek için, şunu kullanırsınız: if (flags & 8) // testler bit 4 ayarlandı
En iyi performans ve sıfır çöp için bunu kullanın:
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;
}
}
}