【问题标题】:C# flag enum toggle by value or nameC# 标志枚举按值或名称切换
【发布时间】:2020-02-24 14:01:25
【问题描述】:

我有一个标志 enum 并希望通过其 (long) 值切换一些值。

[Flags]
public enum Permissions : long
{
    Value0 = 0,
    Value1 = 0x1,
    Value2 = 0x2,
    Value3 = 0x4,
    //etc
}

我通过EF 使用long 字段将实际权限存储在数据库中,例如:

   public Permissions UsersPermissions { get; set; }

从客户端我一一获取值以通过enum切换正确的值,所以如果数据库字段包含

`Value1 | Value2 | Value3`

我得到一个 4 我必须从用户的权限中删除 Value3。

  • 如何通过长版本获得正确的 enum 值?
  • 如何通过长表示来切换 UserPermissions 字段中的一个值?

我试过usersPermissions |= permissionValue,但它说我不能在longPermission 类型上使用|= 运算符。

【问题讨论】:

标签: c# enums bit-manipulation enum-flags


【解决方案1】:

据我所知,您的输入是一个数字,并且您希望它是 Permissions。好吧,即使它不是离散值之一,您也可以从字面上转换它。如果它是一个标志值的组合,那甚至会“弄清楚”。 C# 枚举的这种转换方式非常聪明。

这里有一段代码可以做你想做的事

[Flags]
public enum TestEnum : long
{
    ValueNone = 0,  // You can't really "have" a Value0, it's an absence of values
    Value1 = 0x1,
    Value2 = 0x2,
    Value3 = 0x4,
    Value4 = 0x8
}

foreach(var enumVal in Enum.GetValues(typeof(TestEnum)))
{
    Console.WriteLine("val is: {0} long is: {1}", enumVal, (long)enumVal);
}

{
    Console.WriteLine("Start with 1 and 3, remove 3 from number");
    var testEnum = TestEnum.Value1 | TestEnum.Value3;
    Console.WriteLine("testEnum is: {0}({1})", testEnum, (long)testEnum);
    long testVal = 4;   // TestEnum.Value3
    var removeEnum = (TestEnum)testVal;
    removeEnum = ~removeEnum;
    testEnum &= removeEnum;
    Console.WriteLine("testEnum is: {0}({1})", testEnum, (long)testEnum);
}
{
    Console.WriteLine("Start with 1, 2, 4 and remove 1 and 4 from number");
    var testEnum = TestEnum.Value1 | TestEnum.Value2 | TestEnum.Value4;
    Console.WriteLine("testEnum is: {0}({1})", testEnum, (long)testEnum);
    long testVal = (long)TestEnum.Value1 | (long)TestEnum.Value4;   // Could also have added them
    var removeEnum = (TestEnum)testVal;
    Console.WriteLine("Removing: {0}({1})", removeEnum, (long)removeEnum);
    testEnum &= ~removeEnum;
    Console.WriteLine("testEnum is: {0}({1})", testEnum, (long)testEnum);
}

这是一种通过最后一个元素的值来获取枚举限制的方法:

{
    // Check the value to see what the range of the enumeration is
    var lastVal = Enum.GetValues(typeof(TestEnum)).Cast<TestEnum>().Last();
    long limit = (long)lastVal * 2;
    Console.WriteLine("Limit of flags for TestEnum is: " + limit);
    Func<long, bool> testLambda = x => x < limit;
    Console.WriteLine("Value of {0} within limit: {1}", 14, testLambda(14));
    Console.WriteLine("Value of {0} within limit: {1}", 16, testLambda(16));
    Console.WriteLine("Value of {0} within limit: {1}", 200, testLambda(200));
    Console.WriteLine("Value of {0} within limit: {1}", 0, testLambda(0));
    Console.WriteLine("Out of range looks like: " + (TestEnum)17);
    Console.WriteLine("In range looks like: " + (TestEnum)14);
}

输出:

val is: ValueNone long is: 0
val is: Value1 long is: 1
val is: Value2 long is: 2
val is: Value3 long is: 4
val is: Value4 long is: 8
Start with 1 and 3, remove 3 from number
testEnum is: Value1, Value3(5)
testEnum is: Value1(1)
Start with 1, 2, 4 and remove 1 and 4 from number
testEnum is: Value1, Value2, Value4(11)
Removing: Value1, Value4(9)
testEnum is: Value2(2)
Limit of flags for TestEnum is: 16
Value of 14 within limit: True
Value of 16 within limit: False
Value of 200 within limit: False
Value of 0 within limit: True
Out of range looks like: 17
In range looks like: Value2, Value3, Value4

我完全承认上面可能有更好的方法,但从根本上说,如果你有一个 [Flags] 枚举,因此它里面的所有东西在一定程度上都是“有效的”(所有组合都很好),只需检查看看你的值是否超出范围,然后将它从整数类型直接转换为枚举类型并称之为一天。一旦它成为你的标志类型,你就可以使用位运算符没问题,所以先这样做。

【讨论】:

    【解决方案2】:

    您必须在适当的位置对longPermissions 进行一些转换,如下所示:

    Permissions perms = Permissions.Value1 | Permissions.Value2 | Permissions.Value3;
    Console.WriteLine(perms); // Value1, Value2, Value3
    
    long toRemove = 4;
    // OR: long toRemove = (long) Permissions.Value3;
    
    perms = (Permissions) ((long)perms & ~toRemove); // Use "& ~X" to remove bits set in X.
    
    Console.WriteLine(perms); // Value1, Value2
    

    这里重要的是~toRemove 返回toRemove 的按位补码,从而为您提供一组位标志,您可以&amp; 使用原始值来关闭所有设置的位toRemove.

    您必须将perms 转换为long 才能执行此&amp; 操作。

    最后,通过&amp; 获得所需的结果后,您有一个long,您必须将其转换回Permissions 以获得正确的枚举类型作为最终答案。

    【讨论】:

      【解决方案3】:

      您必须对枚举进行异或操作。因此,如果您的权限值具有:

      • 值1
      • 价值2
      • 价值3
      • 值4,

      如果你想删除 Value4,你必须这样做:

      var perm = Permissions.Value1 | Permissions.Value2 | Permissions.Value3 | Permissions.Value4;
      perm = perm ^ Permissions.Value4;
      

      【讨论】:

      • 我的问题是我得到一个长值而不是 Permissions.Value4。这就是为什么按位运算符不能以这种方式工作的原因。我怎么说perm = perm ^ (long)valueComingFromClient
      • 枚举,在你的例子中,是 long 类型,这是在集线器下使用的类型,所以你需要做一些转换 - perm = (Permissions)((long) perm ^ (long)4)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多