1

1 つの列挙型プロパティ リストを持つアイテムのコレクションがあります。元のプロパティは次のようになります

public class Content {
    List<State> States {get; set;}
}

ここで、「状態」はほぼ 15 のオプションを持つ列挙型です。

Content オブジェクトのコレクションを繰り返し処理しているときに、States プロパティに State.Important や State.Updated などの特定の値が含まれていることを確認し、そこから別の文字列を設定したいと考えています。

何かのようなもの

if(item.States.Has(State.Important) && item.States.Has(State.Updated))
string toProcess = "Do";

Linq または Lambda を使用してこれを行う方法は?

4

7 に答える 7

4

Linq を使用する必要がある場合、これは機能するはずです。

if (item.States.Any(state => state == State.Important) && item.States.Any(state => state == State.Updated))

それ以外の場合はContains()、@ElRonnoco のように使用してください。

(ただし、州がフラグ (2 の累乗) の場合、この答えは少し異なります。)

このアプローチの問題点は、どちらの状態も設定されていない場合、コレクションを完全に 2 回反復処理することです。これが頻繁に発生する場合は、実際よりも遅くなります。

次のように、1回のパスでlinqなしで解決できます。

bool isUpdated = false;
bool isImportant = false;

foreach (var state in item.States)
{
    if (state == State.Important)
        isImportant = true;
    else if (state == State.Updated)
        isUpdated = true;

    if (isImportant && isUpdated)
        break;
}

if (isImportant && isUpdated)
{
    // ...
}

どちらのターゲット状態も設定されていないことが多い非常に大きなリストがない限り、これが問題になる可能性は低いため、とにかく El Ronnoco のソリューションを使用することをお勧めします。

処理する状態が多数ある場合は、次のような拡張メソッドを記述して単純化できます。

public static class EnumerableExt
{
    public static bool AllPredicatesTrueOverall<T>(this IEnumerable<T> self, params Predicate<T>[] predicates)
    {
        bool[] results = new bool[predicates.Length];

        foreach (var item in self)
        {
            for (int i = 0; i < predicates.Length; ++i)
                if (predicates[i](item))
                    results[i] = true;

            if (results.All(state => state))
                return true;
        }

        return false;
    }

この名前を考えるのに少し苦労しました。true各述語について、述語が真であるシーケンス内に少なくとも 1 つの項目がある場合に返されます。しかし、それはメソッド名としては少し長いです... ;)

次に、あなたの例は次のようになります。

if (item.States.AllPredicatesTrueOverall(s => s == State.Important, s => s == State.Updated))

これを使用するサンプルコードを次に示します。

enum State
{
    Unknown,
    Important,
    Updated,
    Deleted,
    Other
}

void run()
{
    IEnumerable<State> test1 = new[]
    {
        State.Important, 
        State.Updated, 
        State.Other, 
        State.Unknown
    };

    if (test1.AllPredicatesTrueOverall(s => s == State.Important, s => s == State.Updated))
        Console.WriteLine("test1 passes.");
    else
        Console.WriteLine("test1 fails.");

    IEnumerable<State> test2 = new[]
    {
        State.Important, 
        State.Other, 
        State.Other, 
        State.Unknown
    };

    if (test2.AllPredicatesTrueOverall(s => s == State.Important, s => s == State.Updated))
        Console.WriteLine("test2 passes.");
    else
        Console.WriteLine("test2 fails.");

    // And to show how you can use any number of predicates:

    bool result = test1.AllPredicatesTrueOverall
    (
        state => state == State.Important,
        state => state == State.Updated,
        state => state == State.Other,
        state => state == State.Deleted
    );
}

しかし、おそらく最も簡単なのは、拡張メソッドを作成することですIEnumerable<State>(心配する状態列挙が 1 つしかない場合)。

public static class EnumerableStateExt
{
    public static bool AllStatesSet(this IEnumerable<State> self, params State[] states)
    {
        bool[] results = new bool[states.Length];

        foreach (var item in self)
        {
            for (int i = 0; i < states.Length; ++i)
                if (item == states[i])
                    results[i] = true;

            if (results.All(state => state))
                return true;
        }

        return false;
    }
}

その後、元のコードは次のようになります。

if (item.States.AllStatesSet(State.Important, State.Updated))

より多くの状態を簡単に指定できます。

if (item.States.AllStatesSet(State.Important, State.Updated, State.Deleted))
于 2013-06-11T09:44:14.970 に答える
2

Linq は必要ありません。思わない

if(item.States.Contains(State.Important) && item.States.Contains(State.Updated))
  string toProcess = "Do";

http://msdn.microsoft.com/en-us/library/bhkz42b3.aspx

于 2013-06-11T09:43:59.617 に答える
1

リストにはContainsメソッドがあるため、コードは次のようになります

if(item.States.Contains(State.Important) && item.States.Contains(State.Updated))
    string toProcess = "Do";

ここでLinqまたはラムダ式を使用しても、実際の利点はありません...

于 2013-06-11T09:44:23.430 に答える
0
 var res = (from items in item
                       where items.States.Has(State.Important) && items.States.Has(State.Updated)
                       select new { NewProcess = "Do" }).ToList();


foreach (var result in res)
    {
        string result = result.NewProcess
    }

これを試して

于 2013-06-11T09:46:20.057 に答える
0

列挙型を flagsのセットとして使用することを検討できるかもしれません。つまり、リストがなくても複数の状態を組み合わせることができます。

[Flags]
public enum State
{
    Important = 1,
    Updated = 2,
    Deleted = 4,
    XXX = 8
    ....
}

public class Content
{
    public State MyState { get; set; }
}

if ((myContent.MyState & State.Important) == State.Important
    && (myContent.MyState & State.Updated) == State.Updated)
{
    // Important AND updated
}
于 2013-06-11T09:47:08.893 に答える
0

あなたは一緒に行くことができます

!(new List<States>{State.Important, State.Updated}.Except(item.States).Any());

それほど短くはありませんが、チェックする状態が多数ある場合は簡単です。

アイテムに必要なすべての状態があることを確認したい限り、最初のリストに新しい状態を追加するだけです。

于 2013-06-11T09:45:09.313 に答える
0

ある種の次の実装

Content obj = new Content();
obj.States = SomeMethod(); 

if(obj.States.Any(h => h == State.Important) && obj.States.Any(h => h == State.Updated))
{
   string toProcess = "Do";
}
于 2013-06-11T09:45:57.703 に答える