12

我正在尝试创建一个辅助方法来列出枚举值中设置的所有位的名称(用于记录目的)。我想要一个方法来返回在某些变量中设置的所有枚举值的列表。在我的例子中

[Flag]
Enum HWResponse
{
   None = 0x0,
   Ready = 0x1,
   Working = 0x2,
   Error = 0x80,
}

我喂它 0x81,它应该为我提供一个IEnumerable<HWResponse>包含{Ready, Error}.

由于我没有找到更简单的方法,所以我尝试编写下面的代码,但无法编译。

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 转换为 Enum。

我做错什么了?有没有更好(更简单)的方法来做到这一点?我怎么能做演员?

另外,我尝试将方法编写为

public static IEnumerable<T> MaskToList<T>(Enum mask) where T:Enum

但 Enum 是一种特殊类型,禁止使用“where”语法(使用 C# 4.0)

4

4 回答 4

27

这是使用 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>();
}
于 2012-04-11T19:13:11.407 回答
3

如果您想要的最终结果是名称的字符串列表,只需调用mask.ToString().

如果枚举是这样定义的,你会怎么做:

[Flags]
enum State
{
    Ready = 1,
    Waiting = 2,
    ReadyAndWaiting = 3
}

至于解决编译器错误,应该这样做:

Enum bit = (Enum)(object)curValueBit;

Jon Skeet 有一个名为unconstrained melody的项目,它允许您在编译后通过重写 IL 来添加枚举约束。这是因为 CLR 支持这样的约束,即使 C# 不支持。

另一个想法:将 GetValues 的返回值直接转换为T[]

foreach(T curValueBit in (T[])Enum.GetValues(typeof (T)))
于 2012-04-11T18:54:16.910 回答
2

基于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 真的是一个枚举)和枚举值的获取放在静态构造函数中,所以这只完成一次(这将是一个性能改进) .

另一件事,我添加了一个可选参数,因此您可以忽略枚举的典型“零”/“无”/“NotApplicable”/“未定义”/等值。

于 2012-07-02T07:44:17.137 回答
1

如果只是做这样的事情怎么办:

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没有编译时间检查。这里的编译器只是“相信”你,希望你知道自己在做什么,所以不会引发编译时错误

于 2012-04-11T19:04:16.543 回答