web-dev-qa-db-ja.com

LINQ:すべてではない、すべてではない

提供された値がリスト内の値と一致するかどうかを確認したいことがよくあります(検証時など):

if (!acceptedValues.Any(v => v == someValue))
{
    // exception logic
}

最近、ReSharperがこれらのクエリを次のように単純化するように求めていることに気付きました。

if (acceptedValues.All(v => v != someValue))
{
    // exception logic
}

明らかに、これは論理的には同一で、おそらく少し読みやすいです(多くの数学を行った場合)、私の質問は次のとおりです。これによりパフォーマンスが低下しますか?

それはそうあるべきだと感じています(つまり、.Any()は短絡のように聞こえますが、.All()はそうではないように聞こえます)が、これを実証するものは何もありません。クエリが同じものを解決するかどうか、ReSharperが私を迷わせているかどうかについて、誰もがより深い知識を持っていますか?

252
Mark

ILSpyによるAllの実装(実際に行ってみたところ、「まあ、その方法は少し似ています...」ではなく、影響ではなく理論を議論していればそうかもしれません)。

public static bool All<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    if (predicate == null)
    {
        throw Error.ArgumentNull("predicate");
    }
    foreach (TSource current in source)
    {
        if (!predicate(current))
        {
            return false;
        }
    }
    return true;
}

ILSpyによるAnyの実装:

public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    if (predicate == null)
    {
        throw Error.ArgumentNull("predicate");
    }
    foreach (TSource current in source)
    {
        if (predicate(current))
        {
            return true;
        }
    }
    return false;
}

もちろん、産生されるILには微妙な違いがあるかもしれません。しかし、いいえ、ありません。 ILはほとんど同じですが、述語の不一致でfalseを返すのに対して、述語の一致でtrueを返すという明らかな反転があります。

もちろん、これはlinq-for-objectsのみです。他のlinqプロバイダーが他のlinqプロバイダーを他のプロバイダーよりもはるかに適切に処理する可能性がありますが、その場合、どちらがより最適な実装を取得したかはかなりランダムです。

if(determineSomethingTrue)if(!determineSomethingFalse)よりシンプルで読みやすいと感じている人だけにルールが伝わるように思われます。そして公平に言えば、私たちが行動したい条件に対してtrueを返す代替の冗長性と複雑性のテストがあるとき、私はしばしばif(!someTest)を混乱させる*という点で彼らは少しポイントを持っていると思います。それでも、私は個人的に、あなたが与える2つの選択肢のうちの1つを他の選択肢よりも優先するものは何も見つけません。

*私が理解していないように混乱していないが、私が理解していない決定のいくつかの微妙な理由があることを心配しているので混乱し、「いいえ、彼らはただやることを決めたそのようにして、もう一度このコードを見ていたのを待ってください...」

323
Jon Hanna

これらの拡張メソッドを使用すると、コードが読みやすくなります。

public static bool None<TSource>(this IEnumerable<TSource> source)
{
    return !source.Any();
}

public static bool None<TSource>(this IEnumerable<TSource> source, 
                                 Func<TSource, bool> predicate)
{
    return !source.Any(predicate);
}

元の代わりに

if (!acceptedValues.Any(v => v == someValue))
{
    // exception logic
}

あなたは言えた

if (acceptedValues.None(v => v == someValue))
{
    // exception logic
}
50
AakashM

結果が決定された後、両方とも列挙を停止するため、両方とも同じパフォーマンスになります-渡された述語がtrueに評価する最初の項目のAny()と、述語がfalseに評価する最初の項目のAll()

23
BrokenGlass

Allは最初の不一致で短絡するため、問題はありません。

微妙な点の1つは、

 bool allEven = Enumerable.Empty<int>().All(i => i % 2 == 0); 

本当です。シーケンス内のすべてのアイテムは偶数です。

このメソッドの詳細については、 Enumerable.All のドキュメントを参照してください。

19
Anthony Pegram

All()は、シーケンスのすべての要素が条件を満たすかどうかを決定します。
Any()は、シーケンスの要素が条件を満たすかどうかを決定します。

var numbers = new[]{1,2,3};

numbers.All(n => n % 2 == 0); // returns false
numbers.Any(n => n % 2 == 0); // returns true
6
emy

他の答えも十分にカバーしているように、これはパフォーマンスに関するものではなく、明確さに関するものです。

両方のオプションを幅広くサポートしています:

if (!acceptedValues.Any(v => v == someValue))
{
    // exception logic
}

if (acceptedValues.All(v => v != someValue))
{
    // exception logic
}

しかし、私は思うこれはより広範なサポートを達成するかもしれない

var isValueAccepted = acceptedValues.Any(v => v == someValue);
if (!isValueAccepted)
{
    // exception logic
}

単純にブール値を計算(および命名)してから、何かを無効にします。

5
Michael Haren

これによると link

Any –少なくとも1つの一致を確認します

すべて–すべてが一致することを確認します

5
rcarvalhoxavier

Enumerable source を見ると、AnyAllの実装が非常に近いことがわかります。

public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
    if (source == null) throw Error.ArgumentNull("source");
    if (predicate == null) throw Error.ArgumentNull("predicate");
    foreach (TSource element in source) {
        if (predicate(element)) return true;
    }
    return false;
}

public static bool All<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
    if (source == null) throw Error.ArgumentNull("source");
    if (predicate == null) throw Error.ArgumentNull("predicate");
    foreach (TSource element in source) {
        if (!predicate(element)) return false;
    }
    return true;
}

唯一の違いはブール否定であるため、1つのメソッドが他のメソッドよりも大幅に高速になる方法はありません。

3
Thomas Ayoub