Foreach döngüsünde null olup olmadığını kontrol edin


97

Aşağıdakileri yapmanın daha güzel bir yolu var mı:
Dosyada boş olması için bir kontrol yapmam gerekiyor. Döngüye devam etmeden önce başlıklar

if (file.Headers != null)
{
  foreach (var h in file.Headers)
  {
   //set lots of properties & some other stuff
  }
}

Kısacası, kodumdaki girinti seviyesinden dolayı foreach'i if'in içine yazmak biraz çirkin görünüyor.

Değerlenecek bir şey

foreach(var h in (file.Headers != null))
{
  //do stuff
}

mümkün?


3
Buraya bir göz atabilirsiniz: stackoverflow.com/questions/6937407/…
Adrian Fâciu


1
@AdrianFaciu Bence bu tamamen farklı. Soru, her biri için yapmadan önce koleksiyonun boş olup olmadığını kontrol eder. Bağlantınız koleksiyondaki öğenin boş olup olmadığını kontrol eder.
rikitikitik


1
C # 8 basitçe bir tür boş-koşullu foreach'e sahip olabilir, yani şöyle bir sözdizimi: foreach? (koleksiyonda var i) {} Bence bu, bunu haklı çıkarmak için yeterince yaygın bir senaryo ve burada anlamlı olduğu dile yapılan son boş-koşullu eklemeler göz önüne alındığında?
mms

Yanıtlar:


126

Tıpkı Rune'un önerisine küçük bir kozmetik ek olarak, kendi uzatma yönteminizi oluşturabilirsiniz:

public static IEnumerable<T> OrEmptyIfNull<T>(this IEnumerable<T> source)
{
    return source ?? Enumerable.Empty<T>();
}

O zaman yazabilirsiniz:

foreach (var header in file.Headers.OrEmptyIfNull())
{
}

İsmi zevkinize göre değiştirin :)


80

Dosyadaki öğelerin türünün Başlıklar T olduğunu varsayarsak, bunu yapabilirsiniz

foreach(var header in file.Headers ?? Enumerable.Empty<T>()){
  //do stuff
}

bu boş bir T if dosya numaralandırması oluşturur. Başlıklar null. Dosya türü sahip olduğunuz bir türse, Headersbunun yerine alıcıyı değiştirmeyi düşünün . nullbilinmeyen değeridir, bu nedenle mümkünse null kullanmak yerine "Öğelerin olmadığını biliyorum" yerine null (/ orijinal) "herhangi bir öğe olup olmadığını bilmiyorum" olarak yorumlanmalıdır, göstermek için boş bir küme kullanın sette hiç öğe olmadığını bildiğinizi. Bu aynı zamanda DRY'er olurdu, çünkü sıfır kontrolünü sık sık yapmanız gerekmeyecek.

Jons önerisinin devamı olarak DÜZENLE , ayrıca yukarıdaki kodu değiştirerek bir uzantı yöntemi de oluşturabilirsiniz.

foreach(var header in file.Headers.OrEmptyIfNull()){
  //do stuff
}

Alıcıyı değiştiremediğiniz durumda, operasyona bir isim vererek (OrEmptyIfNull) niyeti daha net ifade ettiği için bu benim tercihim olur.

Yukarıda bahsedilen uzantı yöntemi, optimize edicinin algılamasını imkansız kılabilir. Özellikle, yöntem aşırı yükleme kullanan IList ile ilgili olanlar ortadan kaldırılabilir.

public static IList<T> OrEmptyIfNull<T>(this IList<T> source)
{
    return source ?? Array.Empty<T>();
}

@ kjbartel'in yanıtı ("stackoverflow.com/a/32134295/401246" adresinde en iyi çözümdür, çünkü şu özelliklere sahip değildir: a) nulltüm döngünün IEnumerable<T>(?? olur), b) her Projeye bir Uzatma Yöntemi eklemeyi gerektirir veya c) null IEnumerablesbaşlamaktan kaçınmayı (Pffft! Puh-LEAZE! SMH.) gerektirir (çünkü nullboş liste, geçerli olduğu anlamına gelir, ancak şu anda, yani, bir Çalışan, Satış dışı olanlar için N / A veya hiç kazanmadıklarında Satışlar için boş olan Komisyonlara sahip olabilir).
Tom

@Tom Tek bir boş kontrol dışında , numaralandırıcının boş olmadığı durumlarda ceza yoktur. Numaralandırılabilirin boş olmadığından emin olurken bu kontrolden kaçınmak imkansızdır. Yukarıdaki kod, Başlıkları IEnumerable , foreachgereksinimlerden daha kısıtlayıcı ancak List<T>bağladığınız yanıtın gerekliliğinden daha az kısıtlayıcı olan bir başlığa gerektirir . Numaralandırılabilirin boş olup olmadığını test etmede aynı performans cezasına sahip olanlar.
Rune FS

"LCD" sorununu, kjbartel'in Yanıtıyla aynı başlıktaki Vlad Bezden'in Cevabına Eric Lippert'in yorumuna dayandırıyordum: "@CodeInChaos: Ah, şimdi ne demek istediğini anlıyorum. Derleyici" foreach "ın yinelendiğini algıladığında Bir List <T> veya bir dizi üzerinden, foreach'i değer türü numaralandırıcıları kullanmak için optimize edebilir veya gerçekten bir "for" döngüsü oluşturabilir. Bir liste veya boş sıra üzerinden numaralandırmaya zorlandığında, " en düşük ortak payda "bazı durumlarda daha yavaş olabilen ve daha fazla bellek baskısı oluşturabilen kodlayıcı ....". Bunun gerektirdiğini kabul edin List<T>.
Tom

@tom Cevabın öncülü şu dosyadır. Başlıklar bir IEnumerable <T> 'dir, bu durumda derleyici optimizasyonu yapamaz. Bununla birlikte, bundan kaçınmak için uzatma yöntemi çözümünü genişletmek oldukça basittir. Düzenlemeye bakın
Rune FS

19

Açıkçası ben şunu tavsiye ederim: nulltesti tamamlayın . Bir nulltest yalnızca bir brfalseveya brfalse.s; her şey (gereksiz testler, ödevler ekstra yöntem çağrıları, çok fazla iş dahil etmek için gidiyor GetEnumerator(), MoveNext(), Dispose()yineleyici üzerinde, vs).

Bir iftest basit, açık ve etkilidir.


1
İlginç bir noktaya değindin Marc, ancak şu anda kodun girinti düzeylerini azaltmak istiyorum. Ancak performansı not almam gerektiğinde yorumunuzu aklımda tutacağım.
Eminem

3
Bu Marc hakkında kısa bir not .. Bu soruyu sorduktan ve bazı performans geliştirmeleri uygulamam gerektiğinden yıllar sonra, tavsiyeniz son derece kullanışlı oldu. Teşekkür ederim
Eminem

13

yineleme iyi olmadan önceki "eğer", bu "güzel" anlambilimlerin birkaçı kodunuzu daha az okunabilir hale getirebilir.

her neyse, girinti sizi rahatsız ediyorsa, kontrol edilip edilmeyeceğini değiştirebilirsiniz:

if(file.Headers == null)  
   return;

ve foreach döngüsüne yalnızca headers özelliğinde gerçek bir değer olduğunda ulaşırsınız.

Düşünebileceğim başka bir seçenek de foreach döngünüzde sıfır birleştirme operatörünü kullanmak ve sıfır denetiminden tamamen kaçınmaktır. örneklem:

List<int> collection = new List<int>();
collection = null;
foreach (var i in collection ?? Enumerable.Empty<int>())
{
    //your code here
}

(koleksiyonu gerçek nesneniz / türünüzle değiştirin)


İf ifadesinin dışında herhangi bir kod varsa ilk seçeneğiniz çalışmayacaktır.
rikitikitik

Katılıyorum, if ifadesinin yeni bir liste oluşturmaya kıyasla uygulanması kolay ve ucuz, sadece kod güzelleştirme için.
Andreas Johansson

11

Standart foreach döngüsünden daha hızlı çalışan Null-conditional Operator ve ForEach () kullanma .
Yine de koleksiyonu Listeye atmanız gerekiyor.

   listOfItems?.ForEach(item => // ... );

4
Bu çözüm çalışır neden açıkça belirten yerine sadece bir kod tek satırlık, Cevabınız etrafında bazı açıklama ekleyiniz
LordWilmore

durumum için en iyi çözüm
Josef Henn

3

Bu senaryolar için güzel bir küçük genişletme yöntemi kullanıyorum:

  public static class Extensions
  {
    public static IList<T> EnsureNotNull<T>(this IList<T> list)
    {
      return list ?? new List<T>();
    }
  }

Üstbilgilerin liste türünde olduğu göz önüne alındığında, şunları yapabilirsiniz:

foreach(var h in (file.Headers.EnsureNotNull()))
{
  //do stuff
}

1
??operatörü kullanabilir ve dönüş ifadesini kısaltabilirsinizreturn list ?? new List<T>;
Rune FS

1
@wolfgangziegler, Eğer doğru anlarsam nullnumunenizdeki teste file.Headers.EnsureNotNull() != nullgerek yoktur ve hatta yanlış mı?
Remko Jansen

0

Bazı durumlarda, kural olarak, varsayılan koleksiyon oluşturucularının boş örnekler döndürdüğünü varsayarak, biraz başka, genel bir varyantı tercih ederim.

Bu yöntemi adlandırmak daha iyi olacaktır NewIfDefault. Yalnızca koleksiyonlar için faydalı olmayabilir, bu nedenle tür kısıtlaması IEnumerable<T>gereksiz olabilir.

public static TCollection EmptyIfDefault<TCollection, T>(this TCollection collection)
        where TCollection: class, IEnumerable<T>, new()
    {
        return collection ?? new TCollection();
    }
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.