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を列挙型にキャストできないと文句を言います。

私は何を間違えましたか?これを行うためのより良い(より簡単な)方法はありますか?どうすればキャストを作ることができますか?

また、私はメソッドを次のように書いてみました

public static IEnumerable<T> MaskToList<T>(Enum mask) where T: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には、制約なしメロディと呼ばれるプロジェクトがあります。これを使用すると、コンパイル後にILを書き換えることで列挙型制約を追加できます。これは、C#がサポートしていなくても、CLRがそのような制約をサポートしているために機能します。

T[]別の考え:GetValuesの戻り値を直接:にキャストする方が効率的です。

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

ゲイブの答えに基づいて、私はこれを思いつきました:

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 が実際に列挙型であることの検証) と静的コンストラクターでの列挙型値の取得を行うため、これは 1 回だけ実行されます (これはパフォーマンスの向上になります) 。 .

もう1つ、オプションのパラメーターを追加して、列挙の典型的な「ゼロ」/「なし」/「適用不可」/「未定義」などの値を無視できるようにしました。

于 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 に答える