枚举上最常见的C#按位操作
就我的一生而言,我不记得如何设置、删除、切换或测试位字段中的位。要么我不确定,要么我把它们弄混了,因为我很少需要它们。所以有一张“小备忘单”就好了 例如:枚举上最常见的C#按位操作,c#,.net,enums,bit-manipulation,flags,C#,.net,Enums,Bit Manipulation,Flags,就我的一生而言,我不记得如何设置、删除、切换或测试位字段中的位。要么我不确定,要么我把它们弄混了,因为我很少需要它们。所以有一张“小备忘单”就好了 例如: flags = flags | FlagsEnum.Bit4; // Set bit 4. 或 您能否给出所有其他常见操作的示例,最好是使用[Flags]枚举的C#语法?C++操作有:&^~(用于and、or、xor和not位操作)。同样有趣的是>>和习惯用法是使用按位或相等运算符设置位: flags |= 0x04; 要清除一点,习惯
flags = flags | FlagsEnum.Bit4; // Set bit 4.
或
您能否给出所有其他常见操作的示例,最好是使用[Flags]枚举的C#语法?C++操作有:&^~(用于and、or、xor和not位操作)。同样有趣的是>>和习惯用法是使用按位或相等运算符设置位:
flags |= 0x04;
要清除一点,习惯用法是使用按位and和否定:
flags &= ~0x04;
有时,您有一个标识位的偏移量,习惯用法是将这些偏移量与左移位结合使用:
flags |= 1 << offset;
flags &= ~(1 << offset);
flags |=1C++语法,假设位0为LSB,假设flags为无符号长:
检查是否已设置:
flags & (1UL << (bit to test# - 1))
设置:
flag |=(1UL要测试位,您将执行以下操作:
(假设标志为32位数字)
测试位:
if((flags & 0x08) == 0x08)
(如果设置了位4,则其为真)
向后切换(1-0或0-1):
将第4位重置为零:我对这些扩展做了更多的工作-
flags = flags & 0xFFFFFF7F;
我写了一些扩展方法来扩展我经常使用的System.Enum…我并不是说它们是防弹的,但它们有帮助…删除了注释
在.NET 4中,您现在可以编写:
flags.HasFlag(FlagsEnum.Bit4)
@德鲁
注意到,除了在最简单的情况下,枚举HasGrand与手工编写代码相比,会带来很大的性能损失。请考虑下面的代码:
[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();
}
}
[标志]
公共枚举测试标志
{
1=1,
二等于二,
三等于四,
四等于八,
五=16,
六=32,
七=64,
八=128,
九等于256,
十=512
}
班级计划
{
静态void Main(字符串[]参数)
{
TestFlags f=TestFlags.Five;/*或任何其他枚举*/
布尔结果=假;
秒表s=Stopwatch.StartNew();
对于(int i=0;i<10000000;i++)
{
结果|=f.HasFlag(TestFlags.Three);
}
s、 停止();
控制台。写入线(秒);//*4793毫秒*
s、 重启();
对于(int i=0;i<10000000;i++)
{
结果|=(f&TestFlags.Three)!=0;
}
s、 停止();
控制台写入线(秒);//*27毫秒*
Console.ReadLine();
}
}
超过1000万次迭代,HasFlags扩展方法所需时间高达4793毫秒,而标准按位实现所需时间为27毫秒。这是受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的内置标志枚举操作非常有限。大多数时间用户都没有弄清楚按位操作逻辑
在.NET4中,方法HasFlag
被添加到Enum
中,这有助于简化用户代码,但不幸的是,它存在许多问题
HasFlag
不是类型安全的,因为它接受任何类型的枚举值参数,而不仅仅是给定的枚举类型
HasFlag
在检查值是否包含枚举值参数提供的全部或任何标志时不明确。顺便说一下
HasFlag
相当慢,因为它需要装箱,这会导致分配和更多的垃圾收集
部分由于.NET对标志枚举的支持有限,我编写了OSS库,它解决了这些问题,并使处理标志枚举变得更加容易
下面是它提供的一些操作以及它们仅使用.NET框架的等效实现
联合旗帜
.NET标志|其他标志
Enums.NETflags.CombineFlags(其他标志)
撤除旗帜
.NETflags和~otherFlags
Enums.NETflags.RemoveFlags(其他标志)
共同旗帜
.NET标志和其他标志
Enums.NETflags.CommonFlags(其他标志)
切换标志
.NET标志^otherFlags
Enums.NETflags.ToggleFlags(其他标志)
有所有的旗帜
.NET(标志和其他标志)=其他标志
或标志.HasFlag(其他标志)
Enums.NETflags.HasAllFlags(其他标志)
有旗帜吗
.NET(标志和其他标志)!=0
Enums.NETflags.HasAnyFlags(其他标志)
拿旗子
.NET
可枚举范围(0,64)
.Where(bit=>((flags.GetTypeCode()==TypeCode.UInt64?(long)(ulong)flags:Convert.ToInt64(flags))&(1L Enum.ToObject(flags.GetType(),1L)要获得最佳性能和零垃圾,请使用以下选项:
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;
}
}
}
我也发现这很有用-有什么想法我可以修改它,使它在任何基础类型上工作吗?这些扩展只是我的一天,我的一周,我的一个月,很可能是我的一年。谢谢大家!大家:一定要查看Hugoware链接到的更新。一组非常好的扩展。很遗憾,它们需要装箱,尽管我想不出一个替代方案它不使用装箱,非常简洁。即使是Enum
上的新hasvag
方法也需要装箱。@Drew:有关避免装箱的方法,请参阅:)问题与c#有关,而不是与c++有关。另一方面,c#使用相同的运算符:为了保护@workmad3,原始标记使用c和c+++1来指出这一点,尽管FlagsEnum
是一个丑陋的名称。:)@吉姆,也许吧。这只是一个样本名称,正如原始问题中所使用的那样,因此您可以在代码中随意更改。我知道!但丑陋的名称就像IE6一样,可能永远不会消失:(@JimSchubert,同样,我只是从原始问题中复制了类型名称,以免混淆问题。这表明所有[标志]
enum应该有多个名称,因此名称FlagsEnum
的问题甚至比丑陋更严重。我也推荐。购买有点贵,但我相信Safari Online
flags = flags ^ 0x08;
flags = flags & 0xFFFFFF7F;
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
flags.HasFlag(FlagsEnum.Bit4)
[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();
}
}
/// 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];
}
}
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))`
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;
}
}
}