C# 反转枚举标志
假设我有以下标志:C# 反转枚举标志,c#,enums,bit-manipulation,flags,C#,Enums,Bit Manipulation,Flags,假设我有以下标志: [Flags] public enum Foo { None = 0, Foo1 = 1, Foo2 = 2, Foo4 = 4, Foo8 = 8 } 现在我有了一个变量foo: var foo = Foo.Foo1 | Foo.Foo4; 我想要得到的是foo的以下反转标志。 这意味着: Foo.Foo2 | Foo.Foo8 我已经试过~接线员了。但由于我的枚举是一个int32值,它将所有32位都反转。但实际上我只需要反转我
[Flags]
public enum Foo
{
None = 0,
Foo1 = 1,
Foo2 = 2,
Foo4 = 4,
Foo8 = 8
}
现在我有了一个变量foo:
var foo = Foo.Foo1 | Foo.Foo4;
我想要得到的是foo
的以下反转标志。
这意味着:
Foo.Foo2 | Foo.Foo8
我已经试过~接线员了。但由于我的枚举是一个int32值,它将所有32位都反转。但实际上我只需要反转我的Foo enum
使用的位
编辑:
Foo1 | Foo4将等于以下位掩码:
00000000 00000000 00000000 00000101
如果使用~运算符反转该位掩码,将得到以下结果:
11111111 11111111 11111111 11111010
我希望得到的结果是:
00000000 00000000 00000000 00001010
如你所见。我只想反转Foo枚举使用的位。不是整个32整数值的所有位。您要做的是组合枚举的所有值,然后用当前值的补码屏蔽它
Foo value = Foo.Foo4;
Foo allValues = (Foo)0;
foreach (var v in Enum.GetValues(typeof(Foo)))
allValues |= (Foo)v;
var compliment = allValues & ~(value);
或者,您可以将这些值与Linq组合,并静态缓存它们以提高性能:
public static class FooHelper
{
private readonly static Foo allValues = ((Foo[])Enum.GetValues(typeof(Foo))).Aggregate((Foo)0, (all, cur) => all | cur);
public static Foo AllValues { get { return allValues ; } }
}
后来:
var foo = Foo.Foo1 | Foo.Foo4;
var compliment = FooHelper.AllValues & ~(foo);
更新
如果希望通用方法组合枚举的所有标志值,可以执行以下操作:
var compliment = EnumHelper.GetAll<Foo>() & ~(value);
var恭维=EnumHelper.GetAll()&~(value);
其中,有关枚举的基本数据缓存在惰性参数化单例实例中:
/// <summary>
/// Contains generic utilities for enums, constrained for enums only.
/// </summary>
public sealed class EnumHelper : Enums<Enum>
{
private EnumHelper()
{
}
}
/// <summary>
/// For use by EnumHelper, not for direct use.
/// </summary>
public abstract class Enums<TEnumBase> where TEnumBase : class, IConvertible
{
// Generic singleton remembering basic properties about specified enums, cached for performance.
sealed class DataSingleton<TEnum> where TEnum : struct, TEnumBase
{
static readonly DataSingleton<TEnum> instance = new DataSingleton<TEnum>();
readonly bool isSigned;
readonly TEnum allValues;
readonly bool hasFlags;
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static DataSingleton()
{
}
DataSingleton()
{
isSigned = GetIsSigned();
allValues = GetAll();
hasFlags = GetHasFlags();
}
static bool GetHasFlags()
{
var attributes = typeof(TEnum).GetCustomAttributes(typeof(FlagsAttribute), false);
return attributes != null && attributes.Length > 0;
}
static bool GetIsSigned()
{
var underlyingType = Enum.GetUnderlyingType(typeof(TEnum));
bool isSigned = (underlyingType == typeof(long) || underlyingType == typeof(int) || underlyingType == typeof(short) || underlyingType == typeof(sbyte));
bool isUnsigned = (underlyingType == typeof(ulong) || underlyingType == typeof(uint) || underlyingType == typeof(ushort) || underlyingType == typeof(byte));
if (!isSigned && !isUnsigned)
throw new InvalidOperationException();
return isSigned;
}
static TEnum GetAll()
{
if (GetIsSigned())
{
long value = 0;
foreach (var v in Enum.GetValues(typeof(TEnum)))
// Not sure I need the culture but Microsoft passes it in Enum.ToUInt64(Object value) - http://referencesource.microsoft.com/#mscorlib/system/enum.cs
value |= Convert.ToInt64(v, CultureInfo.InvariantCulture);
return (TEnum)Enum.ToObject(typeof(TEnum), value);
}
else
{
ulong value = 0;
foreach (var v in Enum.GetValues(typeof(TEnum)))
// Not sure I need the culture but Microsoft passes it in Enum.ToUInt64(Object value) - http://referencesource.microsoft.com/#mscorlib/system/enum.cs
value |= Convert.ToUInt64(v, CultureInfo.InvariantCulture);
return (TEnum)Enum.ToObject(typeof(TEnum), value);
}
}
public bool HasFlags { get { return hasFlags; } }
public bool IsSigned { get { return isSigned; } }
public TEnum AllValues { get { return allValues; } }
public static DataSingleton<TEnum> Instance { get { return instance; } }
}
private static void ThrowOnEnumWithoutFlags<TEnum>(DataSingleton<TEnum> data) where TEnum : struct, TEnumBase
{
if (!data.HasFlags)
{
throw (new ArgumentException("The generic argument [<TEnum>] must be an enumeration with the [FlagsAttribute] applied.", "TEnum: " + typeof(TEnum).FullName));
}
}
public static TEnum GetAll<TEnum>() where TEnum : struct, TEnumBase
{
var data = DataSingleton<TEnum>.Instance;
ThrowOnEnumWithoutFlags<TEnum>(data);
return data.AllValues;
}
}
//
///包含枚举的通用实用程序,仅限于枚举。
///
公共密封类EnumHelper:枚举
{
私有枚举助手()
{
}
}
///
///供EnumHelper使用,不直接使用。
///
公共抽象类枚举,其中TEnumBase:class,IConvertible
{
//通用单例记忆有关指定枚举的基本属性,缓存以提高性能。
密封类DataSingleton,其中TEnum:struct,TEnumBase
{
静态只读DataSingleton实例=新DataSingleton();
只读文件被删除;
只读十个所有值;
只读布尔标志;
//显式静态构造函数告诉C#编译器
//不将类型标记为beforefieldinit
静态DataSingleton()
{
}
DataSingleton()
{
isSigned=GetIsSigned();
allValues=GetAll();
hasFlags=GetHasFlags();
}
静态bool GetHasFlags()
{
var attributes=typeof(TEnum).GetCustomAttributes(typeof(FlagsAttribute),false;
返回属性!=null&&attributes.Length>0;
}
静态bool GetIsSigned()
{
var underyingtype=Enum.getunderyingtype(typeof(TEnum));
bool isSigned=(underyingtype==typeof(long)| | underyingtype==typeof(int)| | underyingtype==typeof(short)| | underyingtype==typeof(sbyte));
bool isUnsigned=(underyingtype==typeof(ulong)| | underyingtype==typeof(uint)| | underyingtype==typeof(ushort)| | underyingtype==typeof(byte));
如果(!isSigned&&!isUnsigned)
抛出新的InvalidOperationException();
拒绝退货;
}
静态TEnum GetAll()
{
if(GetIsSigned())
{
长值=0;
foreach(Enum.GetValues中的var v(typeof(TEnum)))
//不确定我是否需要区域性,但Microsoft在Enum.ToUInt64(对象值)中传递它-http://referencesource.microsoft.com/#mscorlib/system/enum.cs
值|=转换为64(v,CultureInfo.InvariantCulture);
return(TEnum)Enum.ToObject(typeof(TEnum),value);
}
其他的
{
ulong值=0;
foreach(Enum.GetValues中的var v(typeof(TEnum)))
//不确定我是否需要区域性,但Microsoft在Enum.ToUInt64(对象值)中传递它-http://referencesource.microsoft.com/#mscorlib/system/enum.cs
值|=Convert.ToUInt64(v,CultureInfo.InvariantCulture);
return(TEnum)Enum.ToObject(typeof(TEnum),value);
}
}
公共bool HasFlags{get{return HasFlags;}}
public bool IsSigned{get{return IsSigned;}
public TEnum AllValues{get{return AllValues;}}
公共静态DataSingleton实例{get{return Instance;}}
}
私有静态void ThrowOnEnumWithoutFlags(DataSingleton数据),其中TEnum:struct,TEnumBase
{
如果(!data.HasFlags)
{
抛出(new ArgumentException(“泛型参数[]必须是应用了[FlagsAttribute]的枚举。”,“TEnum:+typeof(TEnum).FullName));
}
}
public static TEnum GetAll(),其中TEnum:struct,TEnumBase
{
var data=DataSingleton.Instance;
无尾迹(数据);
返回data.AllValues;
}
}
要反转单个枚举标志,请尝试以下操作
private Foo foos;
private static void FlagInvert(Foo foo)
{var res = foos.HasFlag(foo) ? foos &= ~foo : foos |= foo;}
更优雅的解决方案是使用所有字段的掩码,并对其余字段执行异或操作。 通常,您首先向枚举中添加一个“All”值(这样,如果添加了其他值,您就不太可能忘记更新它),因此在您的情况下:
[Flags]
public enum Foo
{
None = 0,
Foo1 = 1,
Foo2 = 2,
Foo3 = 4,
Foo4 = 8,
All = Foo1 | Foo2 | Foo3 | Foo4 // magic starts here
}
var foo = Foo1 | Foo4;
var fooInverted = ~Foo.All ^ ~foo; // result: Foo2|Foo3
为了检查特定标志(即
if(foo&foo.Foo4==foo.Foo4)…
),您得到的结果可以正常工作。您有哪些情况会导致问题?我有一个枚举,指定当前按下的鼠标按钮。我还需要知道哪些是当前未按下的。因此,如果我可以反转指示当前按下鼠标按钮的标志,这将非常有帮助。但我已经知道如何检查国旗了。。。只需要反转它。如果你反转整个值,那么只有那些没有按下的标志将被设置;我仍然不确定仅仅使用~
什么对你不起作用。你能发布一个代码示例来演示你不想要的行为吗?我已经编辑了我的帖子@DanPuzeyI了解你的编辑,但我不明白你为什么需要这个结果。如果你想做点什么,结果