LINQ: Không phải bất kỳ vs Tất cả Đừng


272

Thường thì tôi muốn kiểm tra xem giá trị được cung cấp có khớp với một trong danh sách không (ví dụ: khi xác thực):

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

Gần đây, tôi nhận thấy ReSharper yêu cầu tôi đơn giản hóa các truy vấn này để:

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

Rõ ràng, điều này là giống hệt nhau về mặt logic, có lẽ dễ đọc hơn một chút (nếu bạn đã làm rất nhiều toán học), câu hỏi của tôi là: điều này có dẫn đến kết quả thực hiện không?

Nó có cảm giác như vậy (nghĩa là .Any()nghe có vẻ ngắn mạch, trong khi .All()âm thanh thì không), nhưng tôi không có gì để chứng minh điều này. Có ai có kiến ​​thức sâu hơn về việc các truy vấn sẽ giải quyết giống nhau không, hoặc liệu ReSharper có khiến tôi lạc lối không?


6
Bạn đã thử phân tách mã Linq để xem nó đang làm gì chưa?
RQDQ

9
Trong trường hợp này tôi thực sự sẽ đi với if (! AcceptValues.Contains (someValue)), nhưng tất nhiên đây không phải là câu hỏi :)
csgero

2
@csgero Tôi đồng ý. Trên đây là một sự đơn giản hóa (có lẽ là đơn giản hóa quá mức) của logic thực sự.
Đánh dấu

1
"Cảm giác như nó nên (tức là .Any () nghe có vẻ ngắn mạch, trong khi. All () nghe có vẻ như không)" - Không dành cho bất kỳ ai có trực giác âm thanh. Sự tương đương logic mà bạn lưu ý ngụ ý rằng chúng có khả năng ngắn mạch như nhau. Suy nghĩ của một khoảnh khắc cho thấy rằng Tất cả có thể thoát ra ngay khi gặp phải trường hợp không đủ điều kiện.
Jim Balter

3
Tôi không đồng ý với ReSharper về điều này. Viết những chuyến tàu hợp lý của tư tưởng. Nếu bạn muốn ném một ngoại lệ nếu thiếu một mục yêu cầu : if (!sequence.Any(v => v == true)). Nếu bạn chỉ muốn tiếp tục nếu mọi thứ phù hợp với một đặc điểm kỹ thuật nhất định : if (sequence.All(v => v < 10)).
Timo

Câu trả lời:


344

Việc thực hiện Alltheo ILSpy (như tôi thực sự đã đi và xem, thay vì "tốt, phương pháp đó hoạt động hơi giống ..." Tôi có thể làm nếu chúng ta đang thảo luận về lý thuyết chứ không phải là tác động).

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;
}

Thực hiện Anytheo ILSpy:

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;
}

Tất nhiên, có thể có một số khác biệt tinh tế trong IL được sản xuất. Nhưng không, không có. IL khá giống nhau nhưng đối với sự đảo ngược rõ ràng là trả về true trên kết hợp vị ngữ so với trả về false trên không khớp vị ngữ.

Tất nhiên đây chỉ là linq-for-object. Có thể một số nhà cung cấp linq khác đối xử với một nhà cung cấp tốt hơn nhiều so với nhà cung cấp khác, nhưng nếu đó là trường hợp, thì khá ngẫu nhiên khi một nhà cung cấp triển khai tối ưu hơn.

Dường như quy tắc chỉ xuất phát từ một người nào đó cảm thấy if(determineSomethingTrue)đơn giản và dễ đọc hơn if(!determineSomethingFalse). Và công bằng mà nói, tôi nghĩ rằng họ có một điểm mà tôi thường thấy if(!someTest)khó hiểu * khi có một bài kiểm tra thay thế về tính dài dòng và độ phức tạp tương đương sẽ trở thành sự thật cho điều kiện chúng ta muốn hành động. Tuy nhiên, thực sự, cá nhân tôi thấy không có gì có lợi cho người khác trong hai lựa chọn thay thế mà bạn đưa ra, và có lẽ sẽ hơi nghiêng về phía trước nếu vị ngữ phức tạp hơn.

* Không khó hiểu như tôi không hiểu, nhưng khó hiểu vì tôi lo lắng rằng có một số lý do tinh tế cho quyết định mà tôi không hiểu, và phải mất một vài bước tinh thần để nhận ra rằng "không, họ chỉ quyết định làm Theo cách đó, đợi xem tôi đã xem lại đoạn mã này để làm gì nữa? ... "


8
Tôi không chắc chắn những gì được thực hiện đằng sau các dòng, nhưng đối với tôi dễ đọc hơn nhiều là: if (không phải bất kỳ) so với if (tất cả không bằng nhau).
VikciaR

49
Có một sự khác biệt LỚN khi bảng liệt kê của bạn không có giá trị. 'Bất kỳ' sẽ luôn trả về SAI và 'Tất cả' luôn trả về ĐÚNG. Vì vậy, nói hơn một là tương đương logic của người khác không hoàn toàn đúng!
Arnaud

44
@Arnaud Anysẽ trở lại falsevà do đó !Anysẽ trở lại true, vì vậy chúng giống hệt nhau.
Jon Hanna

11
@Arnaud Không có ai nhận xét rằng ai và tất cả đều tương đương về mặt logic. Hay nói cách khác, tất cả những người bình luận đều không nói rằng Bất kỳ và Tất cả đều tương đương về mặt logic. Sự tương đương là giữa! Bất kỳ (vị ngữ) và Tất cả (! Vị ngữ).
Jim Balter

7
@MacsDickinson hoàn toàn không phải là một sự khác biệt, bởi vì bạn không so sánh các vị từ trái ngược nhau. Tương đương với !test.Any(x => x.Key == 3 && x.Value == 1)việc sử dụng đó Alltest.All(x => !(x.Key == 3 && x.Value == 1))(thực sự tương đương với test.All(x => x.Key != 3 || x.Value != 1)).
Jon Hanna

55

Bạn có thể tìm thấy các phương thức mở rộng này làm cho mã của bạn dễ đọc hơn:

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);
}

Bây giờ thay vì ban đầu của bạn

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

bạn có thể nói

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

6
Cảm ơn - Tôi đã nghĩ đến việc triển khai những thứ này trong thư viện commons của chúng tôi, nhưng tôi chưa quyết định liệu đó có phải là một ý tưởng hay không. Tôi đồng ý rằng họ làm cho mã dễ đọc hơn, nhưng tôi lo ngại rằng họ không thêm đủ giá trị.
Đánh dấu

2
Tôi đã tìm kiếm Không và không tìm thấy nó. Nó là rất nhiều dễ đọc hơn.
Rhyous

Tôi đã phải thêm kiểm tra null: return source == null || ! nguồn.Any (vị ngữ);
Rhyous

27

Cả hai sẽ có hiệu suất giống hệt nhau vì cả hai đều dừng liệt kê sau khi kết quả có thể được xác định - Any()trên mục đầu tiên, vị từ được thông qua sẽ đánh giá trueAll()trên mục đầu tiên mà vị từ ước tính false.


21

All ngắn mạch trên lần đầu tiên không khớp, vì vậy nó không phải là vấn đề.

Một lĩnh vực của sự tinh tế là

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

Là đúng. Tất cả các mục trong chuỗi là chẵn.

Để biết thêm về phương pháp này, hãy tham khảo tài liệu cho Enumerable.All .


12
Có, nhưng bool allEven = !Enumerable.Empty<int>().Any(i => i % 2 != 0)cũng đúng.
Jon Hanna

1
@Jon về mặt ngữ nghĩa không có! = Tất cả. Vì vậy, về mặt ngữ nghĩa, bạn không có hoặc tất cả, nhưng trong trường hợp. ALL () không chỉ là tập hợp con của tất cả các bộ sưu tập trả về đúng cho tất cả và sự khác biệt đó có thể dẫn đến lỗi nếu bạn không biết về nó. +1 cho Anthony
Rune FS

@RuneFS Tôi không theo dõi. Về mặt ngữ nghĩa và logic "không có nơi nào không đúng sự thật ..." thực sự giống như "tất cả những gì đúng ở chỗ đó". Ví dụ: "không có dự án nào được chấp nhận từ công ty chúng tôi?" sẽ luôn có câu trả lời giống như "nơi tất cả các dự án được chấp nhận từ các công ty khác?" ...
Jon Hanna

... Bây giờ, đúng là bạn có thể có lỗi khi giả sử "tất cả các mặt hàng là ..." có nghĩa là có ít nhất một mặt hàng có ít nhất một mặt hàng hoàn thành thử nghiệm, vì "tất cả các mặt hàng ... "Luôn luôn đúng với tập hợp trống, tôi không tranh cãi gì cả. Tôi đã thêm vào mặc dù vấn đề tương tự có thể xảy ra với giả sử "không có mục nào ..." có nghĩa là ít nhất một mục không hoàn thành bài kiểm tra, vì "không có mục nào ..." cũng luôn đúng với tập hợp trống . Không phải là tôi không đồng ý với quan điểm của Anthony, mà tôi nghĩ nó cũng đúng với hai cấu trúc khác đang được thảo luận.
Jon Hanna

@Jon bạn đang nói logic và tôi đang nói ngôn ngữ học. Bộ não con người không thể xử lý một tiêu cực (trước khi nó xử lý tích cực tại điểm mà sau đó nó có thể phủ nhận nó) vì vậy theo nghĩa đó, có khá nhiều sự khác biệt giữa hai điều này. Điều đó không làm cho logic bạn đề xuất không chính xác
Rune FS

8

All()xác định xem tất cả các yếu tố của một chuỗi thỏa mãn một điều kiện.
Any()xác định xem bất kỳ phần tử nào của chuỗi thỏa mãn điều kiện.

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

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

7

Theo liên kết này

Bất kỳ - Kiểm tra ít nhất một trận đấu

Tất cả - Kiểm tra xem tất cả có khớp không


1
bạn đúng nhưng họ dừng lại cùng một lúc cho một bộ sưu tập nhất định. Tất cả phá vỡ khi điều kiện không thành công và Bất kỳ phá vỡ khi nó phù hợp với vị ngữ của bạn. Vì vậy, về mặt kỹ thuật không có gì khác biệt ngoại trừ theo bối cảnh
WPFKK

6

Vì các câu trả lời khác đã được trình bày rõ: đây không phải là về hiệu suất, mà là về sự rõ ràng.

Có nhiều hỗ trợ cho cả hai lựa chọn của bạn:

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

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

Nhưng tôi nghĩ điều này có thể đạt được sự hỗ trợ rộng hơn :

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

Đơn giản chỉ cần tính toán boolean (và đặt tên cho nó) trước khi phủ nhận bất cứ điều gì làm rõ điều này trong tâm trí tôi.


3

Nếu bạn xem qua Nguồn vô số, bạn sẽ thấy việc triển khai AnyAllkhá gần:

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;
}

Không có cách nào mà một phương thức nhanh hơn đáng kể so với phương pháp kia vì sự khác biệt duy nhất nằm ở sự phủ định boolean, vì vậy thích khả năng đọc hơn so với chiến thắng hiệu suất sai.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.