列出标志枚举中的所有位名
本文关键字:标志 枚举 | 更新日期: 2023-09-27 18:30:50
我正在尝试创建一个辅助方法,用于列出枚举值中设置的所有位的名称(用于日志记录目的)。我想要一个方法,可以返回在某些变量中设置的所有枚举值的列表。在我的示例中
[Flag]
Enum HWResponse
{
None = 0x0,
Ready = 0x1,
Working = 0x2,
Error = 0x80,
}
我0x81喂它,它应该为我提供一个含有{Ready, Error}
的IEnumerable<HWResponse>
。
由于我没有找到更简单的方法,我尝试编写下面的代码,但我无法使其编译。
public static IEnumerable<T> MaskToList<T>(Enum mask)
{
if (typeof(T).IsSubclassOf(typeof(Enum)) == false)
throw new ArgumentException();
List<T> toreturn = new List<T>(100);
foreach(T curValueBit in Enum.GetValues(typeof (T)).Cast<T>())
{
Enum bit = ((Enum) curValueBit); // Here is the error
if (mask.HasFlag(bit))
toreturn.Add(curValueBit);
}
return toreturn;
}
在此版本的代码上,编译器抱怨它无法将 T 强制转换为枚举。
我做错了什么?有没有更好(更简单)的方法可以做到这一点?我怎样才能制作演员表?
另外,我尝试将该方法编写为
public static IEnumerable<T> MaskToList<T>(Enum mask) where T:Enum
但 Enum 是一种特殊类型,禁止使用"where"语法(使用 C# 4.0)
下面是使用 LINQ 编写它的简单方法:
public static IEnumerable<T> MaskToList<T>(Enum mask)
{
if (typeof(T).IsSubclassOf(typeof(Enum)) == false)
throw new ArgumentException();
return Enum.GetValues(typeof(T))
.Cast<Enum>()
.Where(m => mask.HasFlag(m))
.Cast<T>();
}
如果您想要的最终结果是名称的字符串列表,只需调用 mask.ToString()
即可。
如果枚举定义如下,您会怎么做:
[Flags]
enum State
{
Ready = 1,
Waiting = 2,
ReadyAndWaiting = 3
}
至于解决编译器错误,这应该这样做:
Enum bit = (Enum)(object)curValueBit;
Jon Skeet 有一个名为 unbound melody 的项目,它允许您在编译后通过重写 IL 来添加枚举约束。 这之所以有效,是因为 CLR 支持此类约束,即使 C# 不支持。
另一个想法:将 GetValues 的返回值直接强制转换为 T[]
会更有效:
foreach(T curValueBit in (T[])Enum.GetValues(typeof (T)))
基于Gabe的回答,我想出了这个:
public static class EnumHelper<T>
where T : struct
{
// ReSharper disable StaticFieldInGenericType
private static readonly Enum[] Values;
// ReSharper restore StaticFieldInGenericType
private static readonly T DefaultValue;
static EnumHelper()
{
var type = typeof(T);
if (type.IsSubclassOf(typeof(Enum)) == false)
{
throw new ArgumentException();
}
Values = Enum.GetValues(type).Cast<Enum>().ToArray();
DefaultValue = default(T);
}
public static T[] MaskToList(Enum mask, bool ignoreDefault = true)
{
var q = Values.Where(mask.HasFlag);
if (ignoreDefault)
{
q = q.Where(v => !v.Equals(DefaultValue));
}
return q.Cast<T>().ToArray();
}
}
我的组织方式略有不同,即我进行了类型检查(即:验证 T 是否确实是枚举)并在静态构造函数中获取枚举值,因此这只执行一次(这将是一个性能改进)。
另一件事,我添加了一个可选参数,以便您可以忽略枚举的典型"零"/"无"/"不适用"/"未定义"/等值。
我花了一些时间搜索如何将标志枚举值转换为列表。我找到了非常简单的解决方案,也许它会帮助某人。
[Flags]
public enum Tag
{
None = 0,
Stablecoin = 1,
NativeTokens = 2,
Dex = 4
}
var values = Tag.Stablecoin | Tag.Dex;
var str = values.ToString(); //"Stablecoin, Dex"
var list = uniqueNftTagsV2.Split(", "); //{"Stablecoin","Dex"}
如果只是做这样的事情会怎样:
public static IEnumerable<T> MaskToList<T>(Enum mask)
{
if (typeof(T).IsSubclassOf(typeof(Enum)) == false)
throw new ArgumentException();
List<T> toreturn = new List<T>(100);
foreach(T curValueBit in Enum.GetValues(typeof (T)).Cast<T>())
{
Enum bit = (curValueBit as Enum); // The only difference is actually here,
// use "as", instead of (Enum) cast
if (mask.HasFlag(bit))
toreturn.Add(curValueBit);
}
return toreturn;
}
由于as
没有编译时检查。这里的编译器只是"相信"你,希望你知道你在做什么,所以编译时错误不会引发。