最常见的 C# 枚举按位运算

发布于 2024-07-05 08:41:08 字数 348 浏览 7 评论 0原文

对于我的一生,我不记得如何在位字段中设置、删除、切换或测试位。 要么我不确定,要么我把它们搞混了,因为我很少需要这些。 因此,如果有一份“小备忘单”就好了。

例如:

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

或者

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

您能否给出所有其他常见操作的示例,最好是使用 [Flags] 枚举的 C# 语法?

For the life of me, I can't remember how to set, delete, toggle or test a bit in a bitfield. Either I'm unsure or I mix them up because I rarely need these. So a "bit-cheat-sheet" would be nice to have.

For example:

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

or

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

Can you give examples of all the other common operations, preferably in C# syntax using a [Flags] enum?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

苦笑流年记忆 2024-07-12 08:41:09

在 .NET 4 中,您现在可以编写:

flags.HasFlag(FlagsEnum.Bit4)

In .NET 4 you can now write:

flags.HasFlag(FlagsEnum.Bit4)
尐籹人 2024-07-12 08:41:09

习惯用法是使用按位或等于运算符来设置位:

flags |= 0x04;

要清除位,习惯用法是使用按位与取反:

flags &= ~0x04;

有时您有一个偏移量来标识您的位,然后习惯用法是将它们与左移:

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

The idiom is to use the bitwise or-equal operator to set bits:

flags |= 0x04;

To clear a bit, the idiom is to use bitwise and with negation:

flags &= ~0x04;

Sometimes you have an offset that identifies your bit, and then the idiom is to use these combined with left-shift:

flags |= 1 << offset;
flags &= ~(1 << offset);
川水往事 2024-07-12 08:41:09

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

超过 1000 万次迭代,HasFlags 扩展方法花费了高达 4793 毫秒的时间,而标准按位实现只需要 27 毫秒。

@Drew

Note that except in the simplest of cases, the Enum.HasFlag carries a heavy performance penalty in comparison to writing out the code manually. Consider the following 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();
    }
}

Over 10 million iterations, the HasFlags extension method takes a whopping 4793 ms, compared to the 27 ms for the standard bitwise implementation.

铁憨憨 2024-07-12 08:41:09

不幸的是,.NET 的内置标志枚举操作非常有限。 大多数时候,用户需要弄清楚按位运算逻辑。

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

  1. HasFlag 不是类型安全的,因为它接受任何类型的枚举值参数,而不仅仅是给定的枚举类型。
  2. HasFlag 对于是否检查该值是否具有枚举值参数提供的全部或任何标志这一点不明确。 一切都是顺便说一下。
  3. HasFlag 相当慢,因为它需要装箱,这会导致分配,从而导致更多的垃圾收集。

部分由于 .NET 对标志枚举的支持有限,我编写了 OSS 库 枚举。 NET 它解决了所有这些问题并使处理标志枚举变得更加容易。

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

组合标志

.NET otherFlags

Enums.NET flags.CombineFlags(otherFlags)


删除标志

.NET        ;      标志 & ~otherFlags

Enums.NET flags.RemoveFlags(otherFlags)


常见标志

.NET             标志& otherFlags

Enums.NET flags.CommonFlags(otherFlags)


切换标志

.NET        ;      flags ^ otherFlags

Enums.NET flags.ToggleFlags(otherFlags)


具有所有标志

< b>.NET            (flags & otherFlags) == otherFlags 或 < code>flags.HasFlag(otherFlags)

Enums.NET flags.HasAllFlags(otherFlags)


有任何标志

.NET    ;       (flags & otherFlags) != 0

Enums.NET flags.HasAnyFlags(otherFlags)


获取标志

.NET

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 中。 您可以在此处查看我的提案。

.NET's built-in flag enum operations are unfortunately quite limited. Most of the time users are left with figuring out the bitwise operation logic.

In .NET 4, the method HasFlag was added to Enum which helps simplify user's code but unfortunately there are many problems with it.

  1. HasFlag is not type-safe as it accepts any type of enum value argument, not just the given enum type.
  2. HasFlag is ambiguous as to whether it checks if the value has all or any of the flags provided by the enum value argument. It's all by the way.
  3. HasFlag is rather slow as it requires boxing which causes allocations and thus more garbage collections.

Due in part to .NET's limited support for flag enums I wrote the OSS library Enums.NET which addresses each of these issues and makes dealing with flag enums much easier.

Below are some of the operations it provides along with their equivalent implementations using just the .NET framework.

Combine Flags

.NET             flags | otherFlags

Enums.NET flags.CombineFlags(otherFlags)


Remove Flags

.NET             flags & ~otherFlags

Enums.NET flags.RemoveFlags(otherFlags)


Common Flags

.NET             flags & otherFlags

Enums.NET flags.CommonFlags(otherFlags)


Toggle Flags

.NET             flags ^ otherFlags

Enums.NET flags.ToggleFlags(otherFlags)


Has All Flags

.NET             (flags & otherFlags) == otherFlags or flags.HasFlag(otherFlags)

Enums.NET flags.HasAllFlags(otherFlags)


Has Any Flags

.NET             (flags & otherFlags) != 0

Enums.NET flags.HasAnyFlags(otherFlags)


Get Flags

.NET

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


I'm trying to get these improvements incorporated into .NET Core and maybe eventually the full .NET Framework. You can check out my proposal here.

半山落雨半山空 2024-07-12 08:41:09

C++ 语法,假设位 0 为 LSB,假设标志为 unsigned 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))

C++ syntax, assuming bit 0 is LSB, assuming flags is unsigned long:

Check if Set:

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

Check if not set:

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

Set:

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

Clear:

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

Toggle:

flag ^= (1UL << (bit to set# - 1))
夏末的微笑 2024-07-12 08:41:09

为了获得最佳性能和零垃圾,请使用:

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

For the best performance and zero garbage, use this:

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;
        }
    }
}
寒尘 2024-07-12 08:41:09

按位 (Flags) 枚举指南

旧的,但想尝试一下备忘单,即使供我自己参考:

操作语法示例
On|=< /code>e |= EA
关闭&= + ~e &= ~EA
切换^=e ^= EA
测试 (.NET API).HasFlage.HasFlag(EA)
测试(按位)(参见示例)(e & EA) == EA

示例

[Flags]
enum E {
    A = 0b1,
    B = 0b10,
    C = 0b100
}

E e = E.A;        // Assign (e = A)
e |= E.B | E.C;   // Add    (e = A, B, C)
e &= ~E.A & ~E.B; // Remove (e = C) -- alt syntax: &= ~(E.A | E.B)
e ^= E.A | E.C;   // Toggle (e = A)
e.HasFlag(E.A);   // Test   (returns true)

// Testing multiple flags using bit operations:
bool hasAandB = ( e & (E.A | E.B) ) == (E.A | E.B);

奖励:定义一个Flags枚举

通常,我们使用如下所示的整数:

[Flags]
enum E {
    A = 1,
    B = 2,
    C = 4,
    // etc.

但是当我们接近更大的数字时,计算下一个值并不那么容易:

  // ...
  W = 4194304,
  X = 8388608,
  // ..

但是,有一些替代方案:二进制和十六进制文字。

对于二进制,只需在前一个值的末尾附加一个0

[Flags]
enum E {
    A = 0b1,
    B = 0b10,
    C = 0b100,
    // ...
    W = 0b100_0000_0000_0000_0000_0000,
    X = 0b1000_0000_0000_0000_0000_0000,

十六进制也有一个方便的模式,可能看起来不那么难看:循环 1、2、4、8,每次完成迭代后添加一个零。

[Flags]
enum E {
    A = 0x1,
    B = 0x2,
    C = 0x4,
    D = 0x8,
    E = 0x10, // 16
    F = 0x20, // 32, etc.
    // ...
    W = 0x400000,
    X = 0x800000,

Bitwise (Flags) enum guide

Old, but wanted to take a stab at a cheat sheet, even if for my own reference:

OperationSyntaxExample
On|=e |= E.A
Off&= + ~e &= ~E.A
Toggle^=e ^= E.A
Test (.NET API).HasFlage.HasFlag(E.A)
Test (bitwise)(see example)(e & E.A) == E.A

Examples

[Flags]
enum E {
    A = 0b1,
    B = 0b10,
    C = 0b100
}

E e = E.A;        // Assign (e = A)
e |= E.B | E.C;   // Add    (e = A, B, C)
e &= ~E.A & ~E.B; // Remove (e = C) -- alt syntax: &= ~(E.A | E.B)
e ^= E.A | E.C;   // Toggle (e = A)
e.HasFlag(E.A);   // Test   (returns true)

// Testing multiple flags using bit operations:
bool hasAandB = ( e & (E.A | E.B) ) == (E.A | E.B);

Bonus: defining a Flags enum

Typically, we use integers like so:

[Flags]
enum E {
    A = 1,
    B = 2,
    C = 4,
    // etc.

But as we approach larger numbers, it's not as easy to calculate the next value:

  // ...
  W = 4194304,
  X = 8388608,
  // ..

There are a couple of alternatives, however: binary and hexadecimal literals.

For Binary, just append a 0 at the end of the previous value:

[Flags]
enum E {
    A = 0b1,
    B = 0b10,
    C = 0b100,
    // ...
    W = 0b100_0000_0000_0000_0000_0000,
    X = 0b1000_0000_0000_0000_0000_0000,

Hexadecimal also has a handy pattern and might look a bit less ugly: cycle through 1, 2, 4, 8, adding a zero after each complete iteration.

[Flags]
enum E {
    A = 0x1,
    B = 0x2,
    C = 0x4,
    D = 0x8,
    E = 0x10, // 16
    F = 0x20, // 32, etc.
    // ...
    W = 0x400000,
    X = 0x800000,
怀里藏娇 2024-07-12 08:41:09

要测试一下,您可以执行以下操作:
(假设flags是一个32位数字)

测试位:

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

(If bit 4 is set then its true)
Toggle Back (1 - 0 or 0 - 1):

flags = flags ^ 0x08;

Reset Bit 4 to Zero:

flags = flags & 0xFFFFFF7F;

To test a bit you would do the following:
(assuming flags is a 32 bit number)

Test Bit:

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

(If bit 4 is set then its true)
Toggle Back (1 - 0 or 0 - 1):

flags = flags ^ 0x08;

Reset Bit 4 to Zero:

flags = flags & 0xFFFFFF7F;

心的憧憬 2024-07-12 08:41:09

这是受到 Delphi 中使用 Sets 作为索引器的启发,早在那时:

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

  }
}

This was inspired by using Sets as indexers in Delphi, way back when:

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

  }
}
哥,最终变帅啦 2024-07-12 08:41:09

C++ 操作有: & | ^ ~(用于与、或、异或和非按位运算)。 同样感兴趣的是>> 和<<,它们是位移操作。

因此,要测试标志中设置的位,您可以使用:
if (flags & 8) //测试位 4 已设置

C++ operations are: & | ^ ~ (for and, or, xor and not bitwise operations). Also of interest are >> and <<, which are bitshift operations.

So, to test for a bit being set in a flag, you would use:
if (flags & 8) //tests bit 4 has been set

天气好吗我好吗 2024-07-12 08:41:08

我对这些扩展做了更多工作 - 您可以在此处找到代码< /a>

我编写了一些扩展方法来扩展我经常使用的 System.Enum...我并不是说它们是防弹的,但它们有所帮助... 注释已删除...< /em>

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

I did some more work on these extensions - You can find the code here

I wrote some extension methods that extend System.Enum that I use often... I'm not claiming that they are bulletproof, but they have helped... Comments removed...

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

    }
}

Then they are used like the following

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文