允许按位运算的通用约束?

Roy*_*mir 6 c# generics enums

我有一个enum

  public enum Flags {
        COMMAND_MSG = 1,
        COMMAND_FILE = 2,
        COMMAND_ACTION = 4,
      }
Run Code Online (Sandbox Code Playgroud)

现在,假设我设置了多个值,例如:

Flags g = Flags.COMMAND_ACTION |Flags.COMMAND_MSG; 
Run Code Online (Sandbox Code Playgroud)

所以我有一个带有 value 的 int 5

现在,5我想看到它是以下内容的组合:Flags.COMMAND_ACTION |Flags.COMMAND_MSG;

注意,我没有[Flags] 属性,因为我使用的是 protobuff 库,并且枚举是自动生成的。

我尝试过什么:

public string Show (Flags item)
{
  var s="";
  string.Join(",", Enum.GetValues(typeof(Flags))
        .Cast<Flags>() 
        .Select(f=>(f & item) >0 ?f.ToString() :"") //check if bit is set
        .Where(f=>!string.IsNullOrWhiteSpace(f))); //remove empty
  return s;
}
Run Code Online (Sandbox Code Playgroud)

Show(5);显示也是 如此:COMMAND_MSG,COMMAND_ACTION

那么问题出在哪里呢?

我想将其转换为通用扩展方法:

 public static string ToFlags<T>(this int val, T FlagType) where T : ??

    {
        return string.Join(",", Enum.GetValues(typeof(T))
                     .Cast<T>()
                     .Select(enumEntry => (enumEntry & val) > 0 ? enumEntry.ToString() : "")
                     .Where(f => !string.IsNullOrWhiteSpace(f)));
    }
Run Code Online (Sandbox Code Playgroud)

但有一个错误,因为:

在此输入图像描述

问题 :

我应该应用哪种通用约束才能使其发挥作用?

Oli*_*bes 5

对于这种情况,没有适当的泛型类型约束。您可以对ints 应用按位运算,因此将 enum 值转换为 int。T不能直接投射到int;不过,你可以绕道而行object

public static string ToFlags<T>(this T val)
    where T : struct
{
    return string.Join(",", Enum.GetValues(typeof(T))
        .Cast<T>()
        .Select(enumEntry => ((int)(object)enumEntry & (int)(object)val) > 0
            ? enumEntry.ToString() : "")
        .Where(f => !string.IsNullOrWhiteSpace(f)));
}
Run Code Online (Sandbox Code Playgroud)

注:数字也有类似的情况。您不能where T : number对 T 类型的值说出和应用数字运算符。(请参阅下面我的更新)

您也可以创建valaT并且不必传递Tas 方法参数的类型。您已经将其作为泛型类型参数传递。这就足够了。编译器也足够智能来推断T,因此您不必在调用方法时指定它。

// Test
Flags item = Flags.COMMAND_ACTION | Flags.COMMAND_MSG;
Console.WriteLine(item.ToFlags());
Run Code Online (Sandbox Code Playgroud)

您可以指定struct为泛型类型约束,就像struct代表值类型一样。它并不完美,但比完全没有约束要好。


更新

距离提出这个问题已经过去很长时间了。C# 11在接口和 .NET 7 BCL 接口IBitwiseOperators<T, T, T>IEqualityOperators<T, T, bool>中引入了静态虚拟成员。这些接口(以及许多其他接口)是针对数字类型实现的,但不幸的是不是针对枚举实现的。

请参阅:[API 提案]:Enum 应实现 IBitwiseOperators、IEqualityOperators #81664

从 C# 7.3 开始,除了 之外还可以指定Enum 泛型约束struct