Lambdaları anlıyorum ve Func
ve Action
delegeleri . Ama ifadeler beni çok üzüyor.
Hangi durumlarda Expression<Func<T>>
sade bir eski yerine kullanmak isterdiniz Func<T>
?
Lambdaları anlıyorum ve Func
ve Action
delegeleri . Ama ifadeler beni çok üzüyor.
Hangi durumlarda Expression<Func<T>>
sade bir eski yerine kullanmak isterdiniz Func<T>
?
Yanıtlar:
Lambda ifadelerini ifade ağaçları olarak ele almak ve yürütmek yerine içine bakmak istediğinizde. Örneğin, LINQ to SQL ifadeyi alır ve eşdeğer SQL deyimine dönüştürür ve sunucuya gönderir (lambda yürütmek yerine).
Kavramsal olarak, Expression<Func<T>>
bir bambaşka dan Func<T>
. Func<T>
"Bir delegate
" terimi , bir yöntemin neredeyse bir göstergesidir ve lambda ifadesi için bir ağaç veri yapısınıExpression<Func<T>>
belirtir . Bu ağaç yapısı , lambda ifadesinin gerçek şeyi yapmak yerine ne yaptığını açıklar . Temel olarak ifadelerin, değişkenlerin, yöntem çağrılarının, ... bileşimi hakkındaki verileri tutar (örneğin, bu lambda bazı sabit + bazı parametreler gibi bilgileri tutar). Bu açıklamayı gerçek bir yönteme dönüştürmek (ile ) veya başka şeyler (LINQ to SQL örneği gibi) yapmak için kullanabilirsiniz. Lambdaları anonim yöntemler ve ifade ağaçları olarak ele alma eylemi tamamen derleme zamanıdır.Expression.Compile
Func<int> myFunc = () => 10; // similar to: int myAnonMethod() { return 10; }
hiçbir şey almaz ve 10 döndüren bir IL yöntemine etkili bir şekilde derleyecektir.
Expression<Func<int>> myExpression = () => 10;
parametre almayan ve 10 değerini döndüren bir ifadeyi tanımlayan bir veri yapısına dönüştürülür:
Her ikisi de derleme zamanında aynı görünse de, derleyicinin ürettiği şey tamamen farklıdır .
Expression
, belirli bir delege hakkındaki meta bilgileri içerir.
Expression<Func<...>>
yerine kullandığınızda temsilci yok Func<...>
.
(isAnExample) => { if(isAnExample) ok(); else expandAnswer(); }
böyle bir ifade bir ExpressionTree, If-ifadesi için dalları oluşturulur.
Noobs için bir cevap ekliyorum, çünkü bu cevaplar ne kadar basit olduğunu anlayana kadar başımın üzerinde görünüyordu. Bazen 'kafanı etrafına saramazsın' karmaşık olması beklentinizdir.
LINQ-to-SQL genel olarak kullanmaya çalışırken gerçekten sinir bozucu bir 'hata' içine yürüdü kadar farkı anlamak zorunda değildi:
public IEnumerable<T> Get(Func<T, bool> conditionLambda){
using(var db = new DbContext()){
return db.Set<T>.Where(conditionLambda);
}
}
Daha büyük veri kümelerinde OutofMemoryExceptions almaya başlayana kadar bu harika çalıştı. Lambda içindeki kesme noktalarını ayarlamak, masamdaki her satırdan lambda durumumla eşleşme arayan teker teker yinelendiğini fark etmemi sağladı. Bu beni bir süre boğdu, çünkü neden veri tabloma olması gerektiği gibi LINQ-to-SQL yapmak yerine dev bir IEnumerable olarak davranıyor? Aynı şeyi LINQ-MongoDb muadilimde de aynıydı.
Düzeltme sadece Func<T, bool>
dönüşmekti Expression<Func<T, bool>>
, bu yüzden bunun Expression
yerine niye bunun yerine ihtiyaç duyduğunu googled Func
.
Bir ifade, bir temsilciyi kendisi hakkındaki verilere dönüştürür. Yani a => a + 1
"Sol tarafta bir tane var int a
. Sağ tarafta ona 1 eklersiniz." Bu kadar. Şimdi eve gidebilirsin. Açıkçası bundan daha yapılandırılmış, ama aslında tüm bu bir ifade ağacı aslında - başınızı saran bir şey yok.
Bunu anlayarak Expression
, neden LINQ-to-SQL'e ihtiyaç duyulduğu ve bir Func
yeterli olmadığı anlaşılmaktadır . Func
bir SQL / MongoDb / diğer sorguya nasıl dönüştürüleceğinin nitritini görmek için kendi içine girmenin bir yolunu taşımaz. Toplama, çarpma veya çıkarma yapıp yapmadığını göremezsiniz. Yapabileceğiniz tek şey onu çalıştırmak. Expression
Öte yandan, temsilcinin içine bakmanıza ve yapmak istediği her şeyi görmenize olanak tanır. Bu, temsilci bir SQL sorgusu gibi istediğiniz herhangi bir dile çevirmenizi sağlar.Func
benim DbContext lambda ifade içeriğini kör olduğu için işe yaramadı. Bu nedenle lambda ifadesini SQL'e çeviremedi; ancak, bir sonraki en iyi şeyi yaptı ve bu koşullu benim tablodaki her satır üzerinden yineledi.
Düzenleme: John Peter'ın isteğindeki son cümlemle ilgili açıklama:
IQueryable, IEnumerable'ı genişletir, bu nedenle IEnumerable'ın Where()
kabul eden aşırı yükleri alma gibi yöntemleri Expression
. Buna bir Expression
geçtiğinizde, sonuç olarak bir IQueryable tutarsınız, ancak bir geçtiğinizde Func
IEnumerable üssüne geri düşersiniz ve sonuç olarak bir IEnumerable alırsınız. Diğer bir deyişle, fark etmeden veri kümenizi sorgulanacak bir şeyin aksine yinelenecek bir listeye dönüştürdünüz. İmzalara gerçekten bakana kadar bir fark fark etmek zor.
İfade vs Func seçiminde son derece önemli bir husus, LINQ to Entities gibi IQueryable sağlayıcılarının bir İfadede geçtiklerinizi 'sindirebilmeleri', ancak bir Func'ta geçtiklerinizi göz ardı edebilmeleridir. Konuyla ilgili iki blog yayınım var:
İfade ve Varlık Çerçevesi ile Func ve LINQ Aşık Olma - Bölüm 7: İfadeler ve İşlevler (son bölüm) hakkında daha fazla bilgi
Ben arasındaki farklar hakkında bazı notlar eklemek istiyorum Func<T>
ve Expression<Func<T>>
:
Func<T>
sadece normal bir eski okul MulticastDelegate;Expression<Func<T>>
"Lambda" ifadesi, lambda ifadesinin ifade ağacı şeklinde bir temsilidir;Func<T>
;ExpressionVisitor
;Func<T>
;Expression<Func<T>>
.Kod örnekleri ile ayrıntıları açıklayan bir makale vardır:
LINQ: Func <T> vs. İfade <Func <T>> .
Umarım yardımcı olacaktır.
Bu konuda Krzysztof Cwalina'nın kitabından daha felsefi bir açıklama var ( Çerçeve Tasarım Yönergeleri: Sözleşmeler, Deyimler ve Yeniden Kullanılabilir .NET Kütüphaneleri için Desenler );
Resim olmayan sürüm için düzenle:
Çoğu zaman Func veya Action isteyeceksiniz, eğer tek yapmanız gereken bir kod çalıştırmak. İhtiyacınız İfade çalıştırıldığı önce kodu analiz tefrika veya optimize gerektiğinde. İfade kodu düşünmek içindir, Func / Action kodu çalıştırmak içindir.
database.data.Where(i => i.Id > 0)
olarak yürütülmesi gerekir SELECT FROM [data] WHERE [id] > 0
. Sadece bir Func geçmek ederseniz, sürücü üzerinde blinders koyduk ve onu yapabileceği tek şey SELECT *
id> 0'dır tamamlayan ile her şeyi dışarı her ve filtre aracılığıyla, iterate ve daha sonra belleğe, bu verilerin tümünü Dolu bir kez Func
de Expression
güçlendirir sürücüyü analiz edip Func
Sql / MongoDb / başka bir sorguya dönüştürür.
Expression
ama tatil Func/Action
LINQ, kanonik bir örnektir (örneğin, bir veritabanıyla konuşmak), ancak gerçekte, aslında ne yapmaktan ziyade ne yapacağınızı ifade etmeyi önemsediğiniz her zaman . Örneğin, bu yaklaşımı protobuf-net'in RPC yığınında kullanıyorum (kod oluşturma vb. Önlemek - böylece ile bir yöntem çağırır:
string result = client.Invoke(svc => svc.SomeMethod(arg1, arg2, ...));
Bu Çözmek, deyim ağacı dekonstürüksiyon SomeMethod
, (ve her bağımsız değişkenin değeri) RPC çağrısı yapar, herhangi günceller ref
/out
arg'yi ve uzak çağrıdan sonucu döndürür. Bu sadece ifade ağacı ile mümkündür. Bunu burada daha fazla kaplarım .
Başka bir örnek, genel operatörler koduyla yapıldığı gibi, ifade ağaçlarını bir lambda'ya derlemek amacıyla manuel olarak oluşturduğunuz zamandır .
İşlevinizi kod olarak değil veri olarak değerlendirmek istediğinizde bir ifade kullanırsınız. Kodu (veri olarak) değiştirmek istiyorsanız bunu yapabilirsiniz. Çoğu zaman ifadelere ihtiyaç duymazsanız muhtemelen bir tane kullanmanız gerekmez.
Birincil neden, kodu doğrudan çalıştırmak istemediğiniz, daha ziyade incelemek istemenizdir. Bu, çeşitli nedenlerden dolayı olabilir:
Expression
delege olarak serileştirmek imkansız olabilir, çünkü herhangi bir ifade keyfi bir delege / yöntem başvurusunun çağrılmasını içerebilir. "Kolay" elbette görecelidir.
Performanstan bahseden henüz bir cevap göremiyorum. Or Func<>
ye geçmek Where()
ya Count()
da kötüdür. Gerçek kötü. Eğer bir kullanırsanız Func<>
o zaman çağırır IEnumerable
yerine LINQ şeyler IQueryable
bütün tablolar çekti ve almak demek olduğunu, daha sonra süzülür. Expression<Func<>>
özellikle başka bir sunucuyu yaşayan bir veritabanını sorguluyorsanız, çok daha hızlıdır.