LINQ: Hiçbiri Yok Hepsi Yok


272

Genellikle, sağlanan bir değerin bir listedeki değerle eşleşip eşleşmediğini kontrol etmek istiyorum (örn. Doğrulama yaparken):

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

Son zamanlarda, ReSharper'ın bu sorguları basitleştirmemi istediğini fark ettim:

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

Açıkçası, bu mantıksal olarak aynı, belki biraz daha okunabilir (çok fazla matematik yaptıysanız), sorum şu: bu bir performans isabetiyle sonuçlanıyor mu?

Olması gerektiği .Any()gibi geliyor (yani kısa devreler .All()gibi geliyor , oysa öyle değil), ama bunu doğrulayacak hiçbir şeyim yok. Sorguların aynı sorunu çözüp çözmeyeceği veya ReSharper'ın beni yanlış yönlendirip yönlendirmediği konusunda daha derin bilgiye sahip olan var mı?


6
Ne yaptığını görmek için Linq kodunu sökmeyi denediniz mi?
RQDQ

9
Bu durumda ben aslında (! İle kabul edilirValues.Contains (someValue)), ama tabii ki bu soru değildi :)
csgero

2
@csgero katılıyorum. Yukarıdaki gerçek mantığın basitleştirilmesiydi (belki de aşırı basitleştirilmeydi).
Mark

1
"Olması gerektiği gibi geliyor (yani .Any () kısa devreler gibi geliyor, oysa .All () öyle gelmiyor)" - Ses sezgileri olan herkes için değil. Not ettiğiniz mantıksal eşdeğerlik, eşit derecede kısa devre olduklarını gösterir. Bir anın düşüncesi, hak kazanamayan bir durumla karşılaşıldığında Herkes'in vazgeçebileceğini gösteriyor.
Jim Balter

3
Bu konuda ReSharper ile evrensel olarak aynı fikirdeyim. Mantıklı düşünceler yazın. Bir istisna atmak istiyorsanız gerekli bir öğe eksik ise: if (!sequence.Any(v => v == true)). Yalnızca her şeyin uygun olup olmadığını belli şartnameye devam etmek isterseniz: if (sequence.All(v => v < 10)).
Timo

Yanıtlar:


344

AllILSpy'a göre uygulanması ( aslında olduğu gibi "iyi, bu yöntem biraz işe yarıyor ..." yerine, teorinin etkisini değil de tartışmayı düşünürsek yapabilirim).

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

AnyILSpy'a göre uygulama :

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

Tabii ki, üretilen IL'de bazı ince farklar olabilir. Ama hayır, hayır yok. IL hemen hemen aynıdır, ancak yüklem eşleşmesinde doğru dönüşün, ters uyumsuzlukta yanlış döndürülmesinin bariz tersine çevrilmesi için.

Bu sadece nesneler için linq'tir. Diğer bazı linq sağlayıcılarının birine diğerinden çok daha iyi davranması mümkündür, ancak bu durumda, hangisinin daha uygun bir uygulamaya sahip olduğu oldukça rasgele.

Kuralın sadece if(determineSomethingTrue)daha basit ve daha okunabilir bir duyguya indiği anlaşılıyor if(!determineSomethingFalse). Ve adalet içinde, sanırım if(!someTest), hareket etmek istediğimiz koşul için doğru olacak alternatif bir eşit ayrıntı ve karmaşıklık testi olduğunda, genellikle kafa karıştırıcı * bulduğum bir nokta var. Yine de, şahsen verdiğiniz iki alternatifin diğerinden birini tercih edecek hiçbir şey bulamıyorum ve eğer yüklem daha karmaşık olsaydı belki de öncekine çok hafif eğilirdim.

* Anlamadığım gibi kafa karıştırıcı değil, ama anladığım gibi kafa karıştırıcı, anlayamadığım karar için ince bir neden olduğundan endişe duyuyorum ve bunu gerçekleştirmek için birkaç zihinsel atlama gerekiyor "hayır, sadece yapmaya karar verdiler bu şekilde bekle, bu kod parçasına tekrar ne bakıyordum? ... "


8
Çizgilerin arkasında ne yapıldığından emin değilim, ama benim için çok daha okunabilir: if (hiç değilse), if (hepsi eşit değil).
VikciaR

49
Numaralandırmanızda değer olmadığında BÜYÜK bir fark vardır. 'Herhangi' her zaman YANLIŞ döndürür ve 'Tüm' her zaman DOĞRU döndürür. Yani birinden diğerinin mantıksal eşdeğeri demek tamamen doğru değildir!
Arnaud

44
@Arnaud Anygeri dönecek falseve dolayısıyla !Anygeri dönecek true, böylece aynılar .
Jon Hanna

11
@Arnaud Herhangi ve Tümünün mantıksal olarak eşdeğer olduğunu söyleyen herhangi bir kişi yoktur. Ya da başka bir deyişle, yorum yapan herkes Herhangi ve Tümünün mantıksal olarak eşdeğer olduğunu söylemedi. Eşdeğerlik! Herhangi biri (yüklem) ve Tümü (! Yüklem) arasındadır.
Jim Balter

7
@MacsDickinson bu bir fark değil, çünkü karşıt tahminleri karşılaştırmıyorsunuz. Eşdeğer !test.Any(x => x.Key == 3 && x.Value == 1)olduğu kullanım Allolup test.All(x => !(x.Key == 3 && x.Value == 1))(eşdeğer gerçekten olan test.All(x => x.Key != 3 || x.Value != 1)).
Jon Hanna

55

Aşağıdaki uzantı yöntemlerini kodunuzu daha okunaklı bulabilirsiniz:

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

Şimdi orijinalin yerine

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

söyleyebilirdin

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

6
Teşekkürler - Bunları zaten müşterek kütüphanemize uygulamayı düşünüyordum, ancak henüz iyi bir fikir olup olmadığına karar vermedim. Kodun daha okunabilir olmasını sağlarım, ancak yeterli değer katmadıkları konusunda endişeliyim.
Mark

2
Hiçbiri aradım ve bulamadım. Çok daha okunabilir.
Rhyous

Boş kontroller eklemek zorunda kaldım: return source == null || ! source.Any (yüklem);
Rhyous

27

- Her iki sonuç sonrasında durdurma numaralandırma belirlenebilir, çünkü aynı özelliklere sahip olur Any()ilk öğeye geçirilen yüklem değerlendirir için trueve All()için öncül değerlendirir ilk öğeye false.


21

All ilk maçta kısa devreler, bu bir sorun değil.

Bir incelik alanı,

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

Doğru. Dizideki tüm öğeler çifttir.

Bu yöntem hakkında daha fazla bilgi için Numaralandırılabilir belgelerine bakın .


12
Evet, ama bool allEven = !Enumerable.Empty<int>().Any(i => i % 2 != 0)aynı zamanda doğrudur.
Jon Hanna

1
@Jon anlamsal olarak hiçbiri! = Hepsi. Yani semantik olarak ya hiç ya da hepsi yok. Anthony
Rune FS

@RuneFS Takip etmiyorum. Anlamsal ve mantıksal olarak "hiçbiri doğru olmadığında hiçbiri ..." aslında "gerçek olan her yerde" ile aynı değildir. Örneğin "Şirketimizden kabul edilen projelerin hiçbiri nerede?" her zaman "diğer şirketlerin kabul ettiği tüm projeler?" ile aynı cevaba sahip olacaklar ...
Jon Hanna

... Şimdi, "tüm öğeler ..." olduğunu varsaymaktan hatalara sahip olabileceğiniz doğrudur, "tüm öğeler ..." "boş küme için her zaman doğrudur, buna hiç itiraz etmiyorum. Yine de aynı sorunun "öğelerin hiçbiri ..." ifadesinin en az bir öğenin testi yerine getirmediği anlamına geldiği anlamına gelebileceğini ekledim, çünkü "öğelerin hiçbiri ..." de boş küme için her zaman doğrudur . Bu, Anthony'nin görüşüne katılmıyorum, tartışılan iki yapıdan diğeri için de geçerli olduğunu düşünüyorum.
Jon Hanna

@Jon sen mantık konuşuyorsun ve ben dilbilimden bahsediyorum İnsan beyni bir negatifi işleyemez (pozitif olanı hangi noktada işlemeden önce olumsuzlayabilir), bu yüzden ikisi arasında oldukça fark vardır. Bu, önerdiğiniz mantığı yanlış yapmaz
Rune FS

8

All()sekanstaki tüm elemanların bir koşulu sağlayıp sağlamadığını belirler.
Any()sekanstaki herhangi bir elemanın koşulu karşılayıp karşılamadığını belirler.

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

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

7

Bu bağlantıya göre

Herhangi biri - En az bir eşleşme olup olmadığını kontrol eder

Tümü - Tümünün eşleştiğini kontrol eder


1
haklısın ama belirli bir koleksiyon için aynı anda duruyorlar. Koşul başarısız olduğunda tüm kırılır ve yükleminizle eşleştiğinde Herhangi Bir Kırılır. Yani teknik olarak doğal olarak farklı değil
WPFKK

6

Diğer cevapların iyi kapsadığı gibi: bu performansla ilgili değil, netlikle ilgilidir.

Her iki seçeneğiniz için de geniş destek var:

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

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

Ancak bunun daha geniş bir destek sağlayabileceğini düşünüyorum :

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

Herhangi bir şeyi reddetmeden önce sadece boolean'ı hesaplamak (ve adlandırmak) bunu aklımda çok temizler.


3

Numaralandırılabilir kaynağa bakarsanız , uygulanmasının Anyve Alloldukça yakın olduğunu göreceksiniz :

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

Tek bir yöntem boolean bir olumsuzlamada yattığından, bir yöntemin diğerinden önemli ölçüde daha hızlı olması mümkün değildir, bu nedenle yanlış performans kazancı yerine okunabilirliği tercih edin.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.