枚举上最常见的 C# 按位运算
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/93744/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Most common C# bitwise operations on enums
提问by steffenj
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.
在我的一生中,我不记得如何在位域中设置、删除、切换或测试位。要么我不确定,要么我把它们混在一起,因为我很少需要这些。所以有一个“bit-cheat-sheet”会很好。
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?
您能否举出所有其他常见操作的示例,最好是使用 [Flags] 枚举的 C# 语法?
回答by workmad3
C++ operations are: & | ^ ~ (for and, or, xor and not bitwise operations). Also of interest are >> and <<, which are bitshift operations.
C++ 操作是: & | ^ ~ (用于和、或、异或而不是按位运算)。同样感兴趣的是 >> 和 <<,它们是位移操作。
So, to test for a bit being set in a flag, you would use: if (flags & 8) //tests bit 4 has been set
因此,要测试在标志中设置的位,您可以使用: if (flags & 8) //tests bit 4 has been set
回答by Stephen Deken
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);
回答by Petesh
C++ syntax, assuming bit 0 is LSB, assuming flags is unsigned long:
C++ 语法,假设位 0 是 LSB,假设标志是 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))
回答by Nashirak
To test a bit you would do the following: (assuming flags is a 32 bit number)
要测试一下,您可以执行以下操作:(假设标志是 32 位数字)
Test Bit:
测试位:
if((flags & 0x08) == 0x08)
(如果设置了第 4 位,则为真)切换回(1 - 0 或 0 - 1): flags = flags ^ 0x08;
将位 4 重置为零: flags = flags & 0xFFFFFF7F;
回答by Hugoware
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...
我写了一些扩展我经常使用的 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);
}
}
}
}
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
回答by Drew Noakes
In .NET 4 you can now write:
在 .NET 4 中,您现在可以编写:
flags.HasFlag(FlagsEnum.Bit4)
回答by Chuck Dee
@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:
请注意,除了在最简单的情况下,与手动编写代码相比,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();
}
}
Over 10 million iterations, the HasFlags extension method takes a whopping 4793 ms, compared to the 27 ms for the standard bitwise implementation.
超过 1000 万次迭代,HasFlags 扩展方法需要惊人的 4793 毫秒,而标准按位实现需要 27 毫秒。
回答by Tony Tanzillo
This was inspired by using Sets as indexers in Delphi, way back when:
这是受到在 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];
}
}
回答by TylerBrinkley
.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.
不幸的是,.NET 的内置标志枚举操作非常有限。大多数情况下,用户需要弄清楚按位运算逻辑。
In .NET 4, the method HasFlag
was added to Enum
which helps simplify user's code but unfortunately there are many problems with it.
在 .NET 4 中,HasFlag
添加了Enum
该方法有助于简化用户的代码,但不幸的是它存在很多问题。
HasFlag
is not type-safe as it accepts any type of enum value argument, not just the given enum type.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.HasFlag
is rather slow as it requires boxing which causes allocations and thus more garbage collections.
HasFlag
不是类型安全的,因为它接受任何类型的枚举值参数,而不仅仅是给定的枚举类型。HasFlag
关于它是否检查该值是否具有枚举值参数提供的所有或任何标志是不明确的。这都是顺便的。HasFlag
相当慢,因为它需要装箱,这会导致分配,从而导致更多的垃圾收集。
Due in part to .NET's limited support for flag enums I wrote the OSS library Enums.NETwhich addresses each of these issues and makes dealing with flag enums much easier.
部分由于 .NET 对标志枚举的有限支持,我编写了 OSS 库Enums.NET,它解决了这些问题中的每一个并使处理标志枚举变得更加容易。
Below are some of the operations it provides along with their equivalent implementations using just the .NET framework.
下面是它提供的一些操作及其仅使用 .NET 框架的等效实现。
Combine Flags
组合标志
.NET flags | otherFlags
。网 flags | otherFlags
Enums.NETflags.CombineFlags(otherFlags)
枚举.NETflags.CombineFlags(otherFlags)
Remove Flags
移除标志
.NET flags & ~otherFlags
。网 flags & ~otherFlags
Enums.NETflags.RemoveFlags(otherFlags)
枚举.NETflags.RemoveFlags(otherFlags)
Common Flags
通用标志
.NET flags & otherFlags
。网 flags & otherFlags
Enums.NETflags.CommonFlags(otherFlags)
枚举.NETflags.CommonFlags(otherFlags)
Toggle Flags
切换标志
.NET flags ^ otherFlags
。网 flags ^ otherFlags
Enums.NETflags.ToggleFlags(otherFlags)
枚举.NETflags.ToggleFlags(otherFlags)
Has All Flags
有所有标志
.NET (flags & otherFlags) == otherFlags
or flags.HasFlag(otherFlags)
.NET (flags & otherFlags) == otherFlags
或flags.HasFlag(otherFlags)
Enums.NETflags.HasAllFlags(otherFlags)
枚举.NETflags.HasAllFlags(otherFlags)
Has Any Flags
有任何标志
.NET (flags & otherFlags) != 0
。网 (flags & otherFlags) != 0
Enums.NETflags.HasAnyFlags(otherFlags)
枚举.NETflags.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.NETflags.GetFlags()
枚举.NETflags.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.
我正在尝试将这些改进整合到 .NET Core 中,最终可能会整合到完整的 .NET Framework 中。你可以在这里查看我的建议。
回答by Mark Bamford
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;
}
}
}