Kısa cevap:
Alıntı operatör olan operatör kendi işlenen üzerindeki kapatma semantiğini neden . Sabitler sadece değerlerdir.
Tırnakların ve sabitlerin farklı anlamları vardır ve bu nedenle bir ifade ağacında farklı temsilleri vardır . Çok farklı iki şey için aynı temsile sahip olmak son derece kafa karıştırıcı ve hataya açıktır.
Uzun cevap:
Aşağıdakileri göz önünde bulundur:
(int s)=>(int t)=>s+t
Dış lambda, dış lambda parametresine bağlı toplayıcılar için bir fabrikadır.
Şimdi, bunu daha sonra derlenecek ve çalıştırılacak bir ifade ağacı olarak göstermek istediğimizi varsayalım. İfade ağacının gövdesi ne olmalıdır? Derlenmiş durumun bir temsilci mi yoksa bir ifade ağacı mı döndürmesini istediğinize bağlıdır.
İlginç olmayan davayı reddederek başlayalım. Bir temsilcinin geri dönmesini istiyorsak, o zaman Quote veya Constant kullanılıp kullanılmayacağı bir tartışma konusudur:
var ps = Expression.Parameter(typeof(int), "s");
var pt = Expression.Parameter(typeof(int), "t");
var ex1 = Expression.Lambda(
Expression.Lambda(
Expression.Add(ps, pt),
pt),
ps);
var f1a = (Func<int, Func<int, int>>) ex1.Compile();
var f1b = f1a(100);
Console.WriteLine(f1b(123));
Lambda iç içe geçmiş bir lambdaya sahiptir; derleyici, iç lambda'yı dış lambda için oluşturulan işlevin durumu üzerinde kapalı bir işleve temsilci olarak üretir. Bu davayı daha fazla düşünmemize gerek yok.
Diyelim ki derlenmiş durum , iç kısımda bir ifade ağacı döndürmesini istiyoruz . Bunu yapmanın iki yolu vardır: kolay yol ve zor yol.
Zor yol, bunu söylemek yerine
(int s)=>(int t)=>s+t
gerçekten demek istediğimiz
(int s)=>Expression.Lambda(Expression.Add(...
Sonra için ifade ağacı oluşturmak olduğunu üreten, bu pisliği :
Expression.Lambda(
Expression.Call(typeof(Expression).GetMethod("Lambda", ...
blah blah blah, lambda yapmak için düzinelerce yansıma kodu satırı. Alıntı operatörünün amacı, ifade ağacı derleyicisine, verilen lambda'nın, ifade ağacı oluşturma kodunu açıkça oluşturmak zorunda kalmadan bir işlev olarak değil, bir ifade ağacı olarak değerlendirilmesini istediğimizi söylemektir .
Kolay yol:
var ex2 = Expression.Lambda(
Expression.Quote(
Expression.Lambda(
Expression.Add(ps, pt),
pt)),
ps);
var f2a = (Func<int, Expression<Func<int, int>>>)ex2.Compile();
var f2b = f2a(200).Compile();
Console.WriteLine(f2b(123));
Ve gerçekten, eğer bu kodu derler ve çalıştırırsanız doğru cevabı alırsınız.
Alıntı operatörünün, bir dış değişkeni, dış lambda'nın biçimsel bir parametresini kullanan iç lambda üzerinde kapanış semantiğini indükleyen operatör olduğuna dikkat edin.
Soru şudur: Neden Alıntıyı kaldırıp aynı şeyi yaptırmıyorsunuz?
var ex3 = Expression.Lambda(
Expression.Constant(
Expression.Lambda(
Expression.Add(ps, pt),
pt)),
ps);
var f3a = (Func<int, Expression<Func<int, int>>>)ex3.Compile();
var f3b = f3a(300).Compile();
Console.WriteLine(f3b(123));
Sabit, kapanış anlambilimine neden olmaz. Neden yapmalı? Bunun bir sabit olduğunu söyledin . Bu sadece bir değer. Derleyiciye verildiği gibi mükemmel olmalıdır; derleyici, ihtiyaç duyulan yığına sadece bu değerin bir dökümünü oluşturabilmelidir.
Endüklenen bir kapanma olmadığından, bunu yaparsanız, çağrıda 'System.Int32' türünde bir "değişken" s "tanımlı değil" istisnası alırsınız.
(Bir kenara: Alıntılanan ifade ağaçlarından temsilci oluşturma için kod oluşturucuyu henüz gözden geçirdim ve ne yazık ki 2006'da koda eklediğim bir yorum hala var. Bilginize, kaldırılan dış parametrenin anlık görüntüsü , alıntı yapıldığında bir sabite alınır ifade ağacı, çalışma zamanı derleyicisi tarafından bir temsilci olarak yeniden tanımlandı.Kodu tam olarak şu anda hatırlamadığım şekilde yazmamın iyi bir nedeni vardı, ancak dış parametrelerin değerleri üzerinde kapatma getirmenin kötü bir yan etkisi var değişkenleri kapatmak yerine. Görünüşe göre bu kodu miras alan ekip, bu kusuru düzeltmemeye karar verdi, bu nedenle, derlenmiş bir alıntılanmış iç lambdada gözlemlenen kapalı bir dış parametrenin mutasyonuna güveniyorsanız, hayal kırıklığına uğrayacaksınız. Bununla birlikte, hem (1) resmi bir parametreyi mutasyona uğratmak hem de (2) bir dış değişkenin mutasyonuna güvenmek oldukça kötü bir programlama uygulaması olduğundan, programınızı bu iki kötü programlama uygulamasını kullanmayacak şekilde değiştirmenizi tavsiye ederim. gelecek gibi görünmeyen bir düzeltmeyi bekliyorum. Hata için özür dileriz.)
Öyleyse soruyu tekrarlamak için:
C # derleyicisi, yuvalanmış lambda ifadelerini Expression.Quote () yerine Expression.Constant () ve ifade ağaçlarını başka bir sorgu diline (SQL gibi) işlemek isteyen herhangi bir LINQ sorgu sağlayıcısını içeren bir ifade ağacında derlemek için yapılmış olabilirdi. ), özel Quote düğüm tipine sahip bir UnaryExpression yerine Expression türünde bir ConstantExpression arayabilir ve diğer her şey aynı olur.
Haklısın. Biz olabilir aracı tarafından "Bu değerin üzerindeki kapatma semantiğini teşvik" olduğunu anlamsal bilgiyi kodlamak bir bayrak olarak sabit ifadenin türünü kullanarak .
Bu durumda "Sabit", " tür bir ifade ağacı türü olmadıkça ve değer geçerli bir ifade ağacı olmadığı sürece bu sabit değeri kullan" anlamına gelir ; Verilen ifade ağacının içi, şu anda içinde olabileceğimiz herhangi bir dış lambdalar bağlamında kapanış anlambilimini indüklemek için.
Ama neden olur biz o çılgın şey? Alıntı operatörü delice karmaşık bir operatördür ve kullanacaksanız açık bir şekilde kullanılmalıdır . Halihazırda var olan birkaç düzine arasına fazladan bir fabrika yöntemi ve düğüm türü eklememek konusunda cimri olmak için, sabitlere tuhaf bir köşe durumu eklediğimizi, böylece sabitlerin bazen mantıksal sabitler olmasını ve bazen yeniden yazılmalarını öneriyorsunuz. kapanış semantiği ile lambdas.
Ayrıca sabitin "bu değeri kullan" anlamına gelmemesi biraz garip bir etkiye sahip olacaktır. Tuhaf bir nedenden ötürü , yukarıdaki üçüncü vakanın, bir dış değişkene yeniden yazılmamış bir referansı olan bir ifade ağacını dağıtan bir temsilciye bir ifade ağacını derlemesini istediğinizi varsayalım. Neden? Belki de derleyicinizi test ettiğiniz ve sabiti geçmek istediğiniz için, daha sonra başka analizler yapabilmeniz için. Öneriniz bunu imkansız kılacaktır; ifade ağacı türünde olan herhangi bir sabit ne olursa olsun yeniden yazılacaktır. "Sabit" in "bu değeri kullan" anlamına geldiğine dair makul bir beklenti var. "Sabit" bir "dediğimi yap" düğümüdür. Sabit işlemci ' türüne göre söylemek.
Ve elbette şimdi anlama yükünü (yani, sabitin bir durumda "sabit" anlamına gelen karmaşık anlambilimlere sahip olduğunu ve tür sistemindeki bir bayrağa dayalı "kapanış anlambilimini indüklemek" ) her şeyin üzerine yüklediğinizi unutmayın. yalnızca Microsoft sağlayıcıları üzerinde değil, bir ifade ağacının anlamsal analizini yapan sağlayıcı. Bu üçüncü taraf sağlayıcılardan kaç tanesi yanlış anlar?
"Alıntı", "hey dostum, buraya bak, iç içe geçmiş bir lambda ifadesiyim ve bir dış değişkeni kapatırsam tuhaf anlamlara sahibim!" Yazan büyük bir kırmızı bayrak sallıyor. "Sabit" ise "Ben bir değerden başka bir şey değilim; beni uygun gördüğünüz gibi kullanın." Bir şey karmaşık ve tehlikeli olduğunda, bu değerin özel olup olmadığını anlamak için kullanıcıyı tip sistemini kazarak bu gerçeği gizlemek yerine, onu kırmızı bayraklar dalgalandırmak istiyoruz .
Dahası, fazlalıktan kaçınmanın bir amaç olduğu fikri bile yanlıştır. Elbette, gereksiz, kafa karıştırıcı fazlalıklardan kaçınmak bir hedeftir, ancak çoğu fazlalık iyi bir şeydir; fazlalık netlik yaratır. Yeni fabrika yöntemleri ve düğüm türleri ucuzdur . İhtiyaç duyduğumuz kadarını yapabiliriz, böylece her biri tek bir işlemi temiz bir şekilde temsil eder. "Bu alan bu şeye ayarlanmadıkça bu bir anlama gelir, bu durumda başka bir anlama gelir" gibi çirkin numaralara başvurmamıza gerek yok.