我有一个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)
但有一个错误,因为:

问题 :
我应该应用哪种通用约束才能使其发挥作用?
对于这种情况,没有适当的泛型类型约束。您可以对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。