[英]Invert enum 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 位。 但實際上我只需要反轉我的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);
其中關於枚舉的基本數據緩存在一個惰性參數化的單例實例中:
/// <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;
}
}
一個更優雅的解決方案是使用所有字段的掩碼並對剩余部分執行 XOR 操作。 通常,您首先在枚舉中添加一個“全部”值(這樣,如果添加了另一個值,您就不太可能忘記更新它),因此在您的情況下:
[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
要反轉單個 Enum 標志試試這個
private Foo foos;
private static void FlagInvert(Foo foo)
{var res = foos.HasFlag(foo) ? foos &= ~foo : foos |= foo;}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.