1

私のアプリケーションでは、次のようなものがあります。

public enum Locations {
  LocationA,
  LocationB,
  LocationC
}

private List<Locations> _myLocations;

public Int64 PackedLocations {
  get {
    return PackEnumList(this._myLocations);
  }
}

つまり、列挙型 (int に基づく)、それらの列挙値のリスト、およびこれまで省略したメソッドの結果を返す読み取り専用プロパティです。

そのメソッド PackEnumList は、対応する列挙値が一意の列挙値のリストで選択されたかどうかを各 BIT が示す 64 ビット整数を提供することを目的としています。したがって、上記の例では、_myLocations に {Locations.LocationA} という 1 つのアイテムしかない場合、結果は 1 (バイナリ: ...00001) になり、Locations.LocationC をそのリストに追加すると、結果は 5 (バイナリ: ...000101)。実装は今のところ重要ではありませんが (ただし、完了/関心/フィードバックのために以下に含めます)、そのメソッドのシグネチャは次のとおりです。

public Int64 PackEnumList(List<Enum> listOfEnumValues) {
   ...
}

コンパイルすると、「最適なオーバーロードされたメソッド ... に無効な引数があります」というエラーが表示されます。

これは、_myLocations が int 値のリストとして表示されているためだと思いますが、可能であれば、使用されている列挙が他の何かによって裏付けられていても、PackEnumList() が機能することを望みます。

列挙型のリスト/コレクションを受け入れるメソッドを作成するより適切な方法はありますか?

完全を期すために、ここに私がやろうとしていることの残りを示します (これらは共有ユーティリティ クラスにあるため、静的です)。これらはまだ完全にテストされていません (pack メソッドを呼び出すときにコンパイル エラーを回避できないため)。これを行うためのより良い方法があるかもしれません.半分は興味深い問題を解決するために、半分は興味深い問題だと思うからです.

    public static Int64 PackEnumList(List<Enum> listOfEnumValues) {
        BitArray bits = new BitArray(64, defaultValue: false);
        foreach (var value in listOfEnumValues) {
            // get integer value of each Enum in the List:
            int val = Convert.ToInt32(value);
            if (val >= 64) {
                // this enum has more options than we have bits, so cannot pack
                throw new Exception("Enum value out of range for packing: " + val.ToString());
            }
            bits[val] = true;
        }
        var res = new Int64[1];
        bits.CopyTo(res, 0);
        return res[0];
    }

    // (this method is a little farther from the ideal: the resulting list will need
    //     to be matched by the caller to the appropriate List of Enums by casting 
    //     each Int32 value to the Enum object in the list)
    public static List<Int32> UnpackEnumList(Int64 packedValue) {
        string binaryString = Convert.ToString(packedValue, 2);
        List<Int32> res = new List<Int32>();
        for (int pos = 0; pos < binaryString.Length; pos++) {
            if (binaryString[binaryString.Length - pos - 1] == '1') {
                // bit is on
                res.Add(pos);
            }
        }
        return res;
    }
4

4 に答える 4

4

列挙型のリスト/コレクションを受け入れるメソッドを作成するより適切な方法はありますか?

ストレートC#内?いいえ。しかし、あなたはそれをごまかすことができます...

Unconstrained Melodyというプロジェクトがあります。これにより、「T は列挙型でなければならない」または「T はデリゲート型でなければならない」という制約を持つジェネリック メソッドを作成できます。これらは IL レベルで有効な制約ですが、C# では表現できません

基本的に制約のないメロディーは 2 つの部分で構成されています。

  • これらの制約を持つ便利なメソッドのライブラリ。ソース コードは有効なC# を使用して記述されています。これは実際にはそれらの制約を表していませんが、マーカー インターフェイスを使用しています。
  • これらの制約を実際の「言葉にできない」ものに変換する IL 書き換えプロジェクト (醜いが役に立ちます)

(ライブラリのユーザーは、書き換えられたバイナリをそのまま使用することが期待されます。 )

ここでコードにプロジェクトの後半を使用できるようです。それほど楽しいものではありませんが、うまくいくでしょう。ライブラリ部分も役立つ場合があります。

副次的な考えとして、[Flags]代わりに -style 列挙型の使用を検討することをお勧めします。

[Flags]
public enum Locations {
  LocationA = 1 << 0,
  LocationB = 1 << 1,
  LocationC = 1 << 2
}
于 2012-08-01T13:10:27.740 に答える
1

メソッドのシグネチャを次のように変更しますpublic Int64 PackEnumList(IEnumerable<Enum> listOfEnumValues)

そして、次のように呼び出します。

public Int64 PackedLocations 
{
    get { return PackEnumList(this._myLocations.Cast<Enum>()); }
}
于 2012-08-01T13:07:43.973 に答える
1

AList<Enum>は aList<Locations>または aではありませんList<Int32>。リストを処理するには、ジェネリック メソッドを使用します。

    public static void PackEnumList<T>(IEnumerable<T> list) where T : IConvertible
    {
        foreach (var value in list)
            int numeric = value.ToInt32();
        // etc.
    }
于 2012-08-01T13:08:50.000 に答える
0

署名方法を次のように変更します。

public Int64 PackEnumList<T>(IEnumerable<T> listOfEnumValues) where T : struct, IFormattable, IConvertible {
...
}

where T : struct... は、それを列挙型のみに制約します (両方のインターフェースを実装する他の構造体。これはおそらく非常に低いです)。

于 2012-08-01T13:11:21.563 に答える