Uygun Linq nerede hükümler


133

Günlük hayatımda makul miktarda linq yazıyorum, ama çoğunlukla basit ifadeler. Where cümlelerini kullanırken, onları yazmanın birçok yolu olduğunu ve anlayabildiğim kadarıyla her birinin aynı sonuçlara sahip olduğunu fark ettim. Örneğin;

from x in Collection
  where x.Age == 10
  where x.Name == "Fido"
  where x.Fat == true
  select x;

En azından sonuçlar söz konusu olduğunda buna eşdeğer görünüyor:

from x in Collection
  where x.Age == 10 &&
        x.Name == "Fido" &&
        x.Fat == true
  select x;

Yani gerçekten sözdiziminden başka bir fark var mı? Öyleyse, tercih edilen stil nedir ve neden?


203
Boole Fatmülkünüz var mı? Bu çok anlamsız.
Bala R

104
@Bala R: Hey, köpeğiniz şişmansa, köpeğiniz şişmandır.
AR

Şişman olmadığı sürece
bulut

Yanıtlar:


76

İkincisi, koleksiyondaki her bir öğeye karşı değerlendirmek için yalnızca bir koşula sahip olduğu için daha verimli olacaktır; burada, ilkinde olduğu gibi, ilk koşulu önce tüm öğelere uygular ve sonuç (bu noktada daraltılır) ikinci yüklem için kullanılır vb. Sonuçlar her geçişte daralır, ancak yine de birden fazla geçişi içerir.

Ayrıca zincirleme (ilk yöntem), yalnızca yüklemlerinizi ANDing yapıyorsanız çalışacaktır. Bunun gibi bir şey x.Age == 10 || x.Fat == trueilk yönteminizde işe yaramayacaktır.


1
Bu uzantıyı kullanarak zincir ORing koşulları bir şekilde mümkündür: albahari.com/nutshell/predicatebuilder.aspx
jahu

142

DÜZENLEME: LINQ to Objects, beklediğim gibi davranmıyor. Bunun hakkında yazdığım blog yazısı ilginizi çekebilir ...


Ne adlandırılacakları açısından farklıdırlar - ilki şuna eşdeğerdir:

Collection.Where(x => x.Age == 10)
          .Where(x => x.Name == "Fido")
          .Where(x => x.Fat == true)

wheras ikincisi eşdeğerdir:

Collection.Where(x => x.Age == 10 && 
                      x.Name == "Fido" &&
                      x.Fat == true)

Şimdi, gerçekte ne fark yaratan Where, çağrılmanın uygulanmasına bağlıdır . SQL tabanlı bir sağlayıcıysa, ikisinin de aynı SQL'i oluşturmasını beklerdim. LINQ to Objects içindeyse, ikincisi daha az yönlendirme seviyesine sahip olacaktır (dört yerine sadece iki yineleyici dahil olacaktır). Bu dolaylılık seviyelerinin hız açısından önemli olup olmadığı farklı bir konudur.

Tipik olarak where, önemli ölçüde farklı koşulları temsil ettiklerini hissediyorlarsa (örneğin, biri bir nesnenin bir bölümü ile ilgili ve biri tamamen ayrı) birkaç wherecümle ve çeşitli koşullar yakından ilişkili olduğunda (örneğin, belirli bir değer minimumdan büyük ve maksimumdan küçüktür). Temel olarak, herhangi bir küçük performans farkından önce okunabilirliği dikkate almaya değer.


1
@JonSkeet Belki yanılıyorum, ancak Linq'in nerede uygulandığını hızlı bir şekilde gözden geçirdikten sonra, bundan emin değilim. Nested Where, statik bir yöntem olan 'CombinePredicates' ile birleştirilir. Koleksiyon, birleşik yüklem ile tek bir yineleyici tarafından yalnızca bir kez yinelenir. Elbette, func'u birleştirmenin performansa etkisi var, ancak çok sınırlı. İyi misin ?
Cybermaxs

@Cybermaxs: Tam olarak ne olduğundan emin değil misiniz? Koleksiyonun bir kereden fazla yineleneceğini asla önermedim.
Jon Skeet

@JonSkeet evet elbette ama sonunda tüm yüklemler birleştirilir ve yalnızca bir yineleyici dahil edilir. Enumerable.WhereSelectEnumerableIterator'a bakın.
Cybermaxs

Bağlandığınız sayfa şu anda kapalı. Makale hala başka bir yerde duruyorsa lütfen bağlantıyı günceller misiniz? Teşekkürler.
Asad Saeeduddin

2
@Asad: Güncellendi. (Blogum taşındı.)
Jon Skeet

13

İlki uygulanacak:

Collection.Where(x => x.Age == 10)
          .Where(x => x.Name == "Fido") // applied to the result of the previous
          .Where(x => x.Fat == true)    // applied to the result of the previous

Çok daha basit (ve muhtemelen çok daha hızlı) aksine :

// all in one fell swoop
Collection.Where(x => x.Age == 10 && x.Name == "Fido" && x.Fat == true)

6
"Çok daha hızlı" mı? Henüz hangi LINQ uygulamasının dahil olduğunu bile bilmiyoruz, bu nedenle buna herhangi bir performans çıkarımı eklemek zor.
Jon Skeet

Genel durumda, ikincisi yalnızca 1 döngü gerektirir. Bir sağlayıcı ilk örneği düzleştirmeyi seçebilir, ancak bu gerekli değildir.
user7116

2
Nitekim ... ama ikincisi iddia ediyoruz edilir çok daha hızlı. Önemli ölçüde daha hızlı olacağı hiç de net değil - sonuçta, performans farkının önemi bunun nasıl kullanıldığına bağlı olacaktır.
Jon Skeet

1
@Jon: anlaşmazlık yok. Sizin de not ettiğiniz gibi, gerçek LINQ sağlayıcısı olabilir ve ifadeye faydalı optimizasyon dönüşümleri yapar. Ancak ikincisi yalnızca bir döngü gerektirdiği ve boolean kısa devreden faydalandığı düşünüldüğünde, genel anlamda neden "çok daha hızlı" olarak etiketlenmemesi gerektiğini anlamak zor. OP sadece 5 elemente sahipse, benim açımdan tartışmalıyım.
user7116

11

koştuğumda

from c in Customers
where c.CustomerID == 1
where c.CustomerID == 2
where c.CustomerID == 3
select c

ve

from c in Customers
where c.CustomerID == 1 &&
c.CustomerID == 2 &&
c.CustomerID == 3
select c customer table in linqpad

Müşteri tabloma karşı aynı sql sorgusunu çıktı

-- Region Parameters
DECLARE @p0 Int = 1
DECLARE @p1 Int = 2
DECLARE @p2 Int = 3
-- EndRegion
SELECT [t0].[CustomerID], [t0].[CustomerName]
FROM [Customers] AS [t0]
WHERE ([t0].[CustomerID] = @p0) AND ([t0].[CustomerID] = @p1) AND ([t0].[CustomerID] = @p2)

bu yüzden sql'ye çeviride bir fark yoktur ve diğer cevaplarda bunların lambda ifadelerine nasıl dönüştürüleceğini zaten görmüşsünüzdür.


tamam, o zaman bunlardan herhangi birini kullanırsam performans etkisi olmayacağını söylemek istersiniz?
Bimal Das

Aslında WHERE cümleleri zincirlenmiştir. Yani, nasıl yazdığın önemli değil. Performans farkı yoktur.
hastrb

3

Başlığın altına bakıldığında, iki ifade farklı sorgu temsillerine dönüştürülecektir. Bağlı QueryProviderbir Collectionbu uzak optimize veya olmasın olabilir.

Bu bir linq-to-object çağrısı olduğunda, birden fazla where cümlesi, birbirinden okuyan bir IEnumerables zincirine yol açar. Tek cümleli formu kullanmak burada performansa yardımcı olacaktır.

Temel sağlayıcı bunu bir SQL ifadesine çevirdiğinde, her iki değişkenin de aynı ifadeyi yaratma olasılığı yüksektir.

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.