39

c# .NET 2.0 で何か方法はありますか! 複数の述語を組み合わせるには?

次のコードがあるとしましょう。

List<string> names = new List<string>();
names.Add("Jacob");
names.Add("Emma");
names.Add("Michael");
names.Add("Isabella");
names.Add("Ethan");
names.Add("Emily");

List<string> filteredNames = names.FindAll(StartsWithE);

static bool StartsWithE(string s)
{
    if (s.StartsWith("E"))
    {
        return true;
    }
    else
    {
        return false;
    }
}

これは私に与えます:

Emma
Ethan
Emily

これはかなりクールなものですが、複数の述語を使用してフィルタリングできるようにしたいことはわかっています。

ですから、次のように言えるようになりたいです。

List<string> filteredNames = names.FindAll(StartsWithE OR StartsWithI);

取得するには:

Emma
Isabella
Ethan
Emily

どうすればこれを達成できますか?現在、完全なリストを 2 回フィルタリングし、後で結果を結合しています。しかし残念ながら、これは非常に非効率的であり、さらに重要なことに、元の並べ替え順序が失われます。これは私の状況では受け入れられません。

また、非常に多くのフィルター/述語が存在する可能性があるため、任意の数のフィルター/述語を反復できる必要があります。

繰り返しますが、.NET 2.0 ソリューションである必要があります。残念ながら、新しいバージョンのフレームワークは使用できません。

どうもありがとう。

4

7 に答える 7

67

どうですか:

public static Predicate<T> Or<T>(params Predicate<T>[] predicates)
{
    return delegate (T item)
    {
        foreach (Predicate<T> predicate in predicates)
        {
            if (predicate(item))
            {
                return true;
            }
        }
        return false;
    };
}

そして完全を期すために:

public static Predicate<T> And<T>(params Predicate<T>[] predicates)
{
    return delegate (T item)
    {
        foreach (Predicate<T> predicate in predicates)
        {
            if (!predicate(item))
            {
                return false;
            }
        }
        return true;
    };
}

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

List<string> filteredNames = names.FindAll(Helpers.Or(StartsWithE, StartsWithI));

もう1つの方法は、マルチキャストデリゲートを使用しGetInvocationList()、を使用してそれらを分割してから、同じことを行うことです。次に、次のことができます。

List<string> filteredNames = names.FindAll(Helpers.Or(StartsWithE+StartsWithI));

私は後者のアプローチの大ファンではありませんが、マルチキャストの乱用のように感じます。

于 2009-08-08T07:34:58.860 に答える
37

次のように書けると思います。

Func<string, bool> predicate1 = s => s.StartsWith("E");
Func<string, bool> predicate2 = s => s.StartsWith("I");
Func<string, bool> combinedOr = s => (predicate1(s) || predicate2(s));
Func<string, bool> combinedAnd = s => (predicate1(s) && predicate2(s));

... 等々。

于 2012-10-30T06:40:59.073 に答える
1

.NET 2.0には、そこで使用できる匿名のデリゲートがあります。

List<string> filteredNames = names.FindAll(
   delegate(string s) { return StartsWithE(s) OR StartsWithI(s); }
);

実際、これを使用して関数を置き換えることもできます。

List<string> filteredNames = names.FindAll(
   delegate(string s) { return s.StartsWith("E") || s.StartsWith("I"); }
);
于 2009-08-08T07:35:09.813 に答える
0

述語メソッドをクラスにラップし、コンストラクターにテスト対象の文字列の配列を受け入れるようにさせることができます。

class StartsWithPredicate
{
    private string[] _startStrings;
    public StartsWithPredicate(params string[] startStrings)
    {
        _startStrings = startStrings;
    }
    public bool StartsWith(string s)
    {
        foreach (var test in _startStrings)
        {
            if (s.StartsWith(test))
            {
                return true;
            }
        }
        return false;
    }
}

次に、次のような呼び出しを行うことができます。

List<string> filtered = names.FindAll((new StartsWithPredicate("E", "I")).StartsWith);

そうすれば、メソッドの新しいバリエーションでコード ベースを拡張する必要なく、入力文字列の任意の組み合わせをテストできますStartsWith

于 2009-08-08T07:34:48.097 に答える
0

結果を内部的に論理和する 3 番目の述語を作成できます。ラムダ式を使用してその場でこれを行うことができると思います。このようなもの(私はそのsnytaxがあまり得意ではないので、これはラムダ式ではありません):

static bool StartsWithEorI(string s)
{
    return StartsWithE(s) || StartsWithI(s);
}
于 2009-08-08T07:23:04.347 に答える