2011-06-05 17 views
5
var res = new int[1000000].Skip(999999).First(); 

Bu sorgu yalnızca 999999 girdisini geçmek yerine dizinleyiciyi kullanırsa harika olur.Neden LINQ'taki Skip() nesneler için optimize edilmiş?

System.Core.dll dosyasına bir göz attım ve Skip()'un aksine Count() uzantı yönteminin optimize edildiğini fark ettim. IEnumerable, ICollection uygularsa, yalnızca Count özelliğini çağırır.

cevap

3

benzer bir soruya my answer bakarsak, bunun olmayan bir naif sağlamak kolay olmalı sanki görünen tüm IList için Skip optimizasyonunu (yani uygun istisnalar atar):

public static IEnumerable<T> Skip<T>(this IList<T> source, int count) 
    { 
     using (var e = source.GetEnumerator()) 
      while (count < source.Count && e.MoveNext()) 
       yield return source[count++]; 
    } 

Elbette, örneğiniz bir dizi kullanır. Diziler yineleme sırasında istisnalar atmadığından, işlevim kadar karmaşık bir şey yapmak bile gereksiz olur. Biri, MS'in bunu optimize etmediği, çünkü bunu düşünmedikleri veya optimize etmeye değecek kadar ortak bir durum olduğunu düşünmedikleri sonucuna varabilir.

+0

Ayrıca Listeler yineleyici MoveNext() yanlış bir şekilde ama yine de bir kesmek gibi görünüyor. Yine de harika bir fikir. – codymanix

3

Jon Skeet bu cevap vereceğiz: Bizim dizisi listesidir

, biz sadece düz bunun doğru parçasına atlamak ve bir Öğeleri birer birer verebilmesidir. Bu kulağa harika geliyor, ama eğer liste tekrarlanırken liste değişiyorsa (veya hatta kesiliyorsa)? Basit yineleyici ile çalışan bir uygulama, değişiklik yinelemeyi geçersiz kılacağından genellikle bir istisna atar. Bu kesinlikle davranışsal bir değişim. Skip hakkında ilk yazdığımda, bunu "olası" bir optimizasyon olarak ekledim ve aslında bu dosyayı Edulinq kaynak kodunda çalıştırdım. Şimdi bir hata olduğuna inanıyorum ve tamamen kaldırdık.

... Bu "optimizasyonlar" her ikisi ile

sorun, ertelenmiş yürütme için kullanılan bir yineleyici bloğu içinde Liste tabanlı optimizasyonlar uyguluyorsanız olduğunu tartışmasız olduğunu. Listeler, başlangıç ​​yöntemi çağrısı noktasında ya da hemen yürütme operatörü (Sayım, Takipçi vb.) Içinde ön taraf için optimize edilir, çünkü yöntemin yürütülmesi sırasında sıranın değişmeyeceğini varsayıyoruz. Bu varsayımı bir yineleme bloğu ile yapamayız çünkü kodun akışı çok farklıdır: kodumuz, arayanın MoveNext() işlevini kullanarak tekrar tekrar ziyaret edilir.

https://msmvps.com/blogs/jon_skeet/archive/2011/01/26/reimplementing-linq-to-objects-part-40-optimization.aspx

+1

Teklifiniz listelerle ilgili; OP'nin örneği diziler hakkındadır. Diziler sabit uzunlukta olduklarından, yineleme sırasında değiştirilemezler ve numaralandırıcılar hiçbir istisna alamazlar. – Gabe

+0

@Gabe - Aslında, bu makalenin başlığı "Nesneler için LINQ'da neyi optimize edemeyiz?" Bu makalede Linq'den Objects to – IAmTimCorey

+0

'u kullanmakla ilgili olduğunu düşündüğümden beri, bu konuda hemfikir değilim, her zaman LINQ IEnumerables'ın salt okunur olarak kabul edilmesi gerektiğini düşündüm. Hala anlamadım. Skip'in optimize edilmiş versiyonu hala bir Enumerator döndürüyor (Just index at), bu yüzden Liste değiştiğinde doğru davranıyor. – codymanix

İlgili konular