对枚举最常见的C#按位操作

对于我的生活,我不记得如何设置,删除,切换或testing位字段。 要么我不确定,要么混合起来,因为我很less需要这些。 所以一个“一点点小抄”将是很好的。

例如:

flags = flags | FlagsEnum.Bit4; // Set bit 4. 

要么

 if ((flags & FlagsEnum.Bit4)) == FlagsEnum.Bit4) // Is there a less verbose way? 

你可以给所有其他常用操作的例子,最好在C#语法中使用[Flags]枚举?

我对这些扩展做了更多的工作 – 你可以在这里find代码

我写了一些扩展方法,扩展System.Enum,我经常使用…我并不是声称他们是防弹的,但他们已经帮助… 评论删除…

 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); } } } } 

然后他们像下面一样使用

 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中,您现在可以编写:

 flags.HasFlag(FlagsEnum.Bit4) 

成语是使用按位或相等的运算符来设置位:

 flags |= 0x04; 

要清楚一点,这个习惯用法是按位和用否定:

 flags &= ~0x04; 

有时候你有一个偏移量来识别你的位,然后这个习惯用法是把这些和左移相结合:

 flags |= 1 << offset; flags &= ~(1 << offset); 

@Drew

请注意,除了最简单的情况之外,与手动编写代码相比,Enum.HasFlag会带来严重的性能损失。 考虑下面的代码:

 [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(); } } 

超过一千万次迭代,HasFlags扩展方法需要高达4793毫秒,而标准按位实现需要27毫秒。

C ++语法,假设位0是LSB,假定标志是无符号的long:

检查是否设置:

 flags & (1UL << (bit to test# - 1)) 

检查是否设置:

 invert test !(flag & (...)) 

组:

 flag |= (1UL << (bit to set# - 1)) 

明确:

 flag &= ~(1UL << (bit to clear# - 1)) 

切换:

 flag ^= (1UL << (bit to set# - 1)) 

要testing一下,你可以这样做:(假设标志是一个32位的数字)

testing位:

 if((flags & 0x08) == 0x08) 

(如果第4位被设置,则其为真)切换回(1 – 0或0 – 1):

 flags = flags ^ 0x08; 

将第4位重置为零:

 flags = flags & 0xFFFFFF7F; 

这是受delphi使用集合作为索引器的启发,当时:

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

.NET的内置标志枚举操作不幸的是非常有限。 大多数情况下,用户需要确定按位操作逻辑。

在.NET 4中, HasFlag方法被添加到Enum ,这有助于简化用户的代码,但不幸的是它有许多问题。

  1. HasFlag不是types安全的,因为它接受任何types的枚举值参数,而不仅仅是给定的枚举types。
  2. HasFlag对于检查值是否包含枚举值参数提供的所有或任何标志都是不明确的。 这一切顺便。
  3. HasFlag是相当慢,因为它需要拳击导致分配,从而更多的垃圾收集。

部分原因是.NET对标志枚举的有限支持,我写了OSS库Enums.NET ,它解决了这些问题,并且使标志枚举的处理更容易。

下面是它提供的一些操作以及使用.NET框架的等效实现。

结合标志

。净

flags | otherFlags

Enums.NET

flags.CombineFlags(otherFlags)


删除标志

。净

flags & ~otherFlags

Enums.NET

flags.RemoveFlags(otherFlags)


共同的旗帜

。净

flags & otherFlags

Enums.NET

flags.CommonFlags(otherFlags)


切换标志

。净

flags ^ otherFlags

Enums.NET

flags.ToggleFlags(otherFlags)


有所有的旗帜

。净

(flags & otherFlags) == otherFlagsflags.HasFlag(otherFlags)

Enums.NET

flags.HasAllFlags(otherFlags)


有任何标志

。净

(flags & otherFlags) != 0

Enums.NET

flags.HasAnyFlags(otherFlags)


获取标志

。净

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


我正试图将这些改进融入到.NET Core中,最终可能是完整的.NET Framework。 你可以在这里查看我的build议。

C ++操作是:&| ^〜(for,或者,xor,而不是按位操作)。 另外感兴趣的是“和”,它们是移位操作。

所以,要testing标志位的设置,可以使用:if(flags&8)//testing位4已经设置