it-swarm-tr.com

Enums üzerinde en yaygın C # bitwise işlemleri

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?

196
steffenj

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

.NET 4'te şimdi yazabilirsiniz:

flags.HasFlag(FlagsEnum.Bit4)
106
Drew Noakes

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

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

22
Chuck Dee

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

  1. 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.
  2. 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.
  3. 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.

Bayrakları Birleştir

.AĞ flags | otherFlags

Enums.NET flags.CombineFlags(otherFlags)


Bayrakları Kaldır

.AĞ flags & ~otherFlags

Enums.NET flags.RemoveFlags(otherFlags)


Ortak Bayraklar

.AĞ flags & otherFlags

Enums.NET flags.CommonFlags(otherFlags)


Bayrakları Değiştir

.AĞ flags ^ otherFlags

Enums.NET flags.ToggleFlags(otherFlags)


Tüm Bayraklara Sahip

.AĞ (flags & otherFlags) == otherFlags veya flags.HasFlag(otherFlags)

Enums.NET flags.HasAllFlags(otherFlags)


Herhangi Bir Bayrak Var

.AĞ (flags & otherFlags) != 0

Enums.NET flags.HasAnyFlags(otherFlags)


Bayrakları Al

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

9
TylerBrinkley

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

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

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

  }
}
2
Tony Tanzillo

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ı

0
workmad3

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