2013-11-26 16 views
5

İfade ağaçlarını kullanarak bir lambda ifadesi oluşturmaya çalışıyorum. - formatlama mantığı zaten bir örneği şeklinde bana teslim edilirLambda Expression'u Ekspresyon Ağacı'nın bir parçası olarak değerlendirin

Func<DateTime, string> requiredLambda = dt => 
    { 
     var formattedDate = dt.ToShortDateString(); 

     /** 
     * The logic is not important here - what is important is that I 
     * am using the result of the executed lambda expression. 
     * */ 
     var builder = new StringBuilder(); 
     builder.Append(formattedDate); 
     builder.Append(" Hello!"); 
     return builder.ToString(); 
    }; 

yakalamak sıfırdan yukarı Bu ağacı bina değilim ki: Bu lambda ifade biçimi ı oluşturmak çalışıyorum olduğunu Expression<Func<DateTime, string>> - ki:

Expression<Func<DateTime, string>> formattingExpression = dt => dt.ToShortDateString(); 

Ben ifade ağacının dışında ben

formattingExpression.Compile()(new DateTime(2003, 2, 1)) 

çağırabilir biliyoruz İfadeyi değerlendirmek - ancak konu, ifade ağacında ifadesini içine atabilmek ve bu sayede ifade ağacında sonuç üzerinde ek mantığı gerçekleştirmeme izin vermek. neredeyse kesin aşağı ifade ağaçları nasıl çalıştığını anlaşmazlıktan - Ben şimdiye kadar ile geldi

Hiçbir şey yolculuk yapmak gibi görünüyor. Herhangi bir yardım büyük takdir!

+0

Anlamıyorum. İfade ağacının içinde neden formattingExpression.Compile() 'yi çağırmıyorsunuz? – svick

cevap

3

Yani, istediğiniz Sonra

Func<Func<DateTime,string>, DateTime, string> requiredLambda = (f, dt) => 
{ 
    var formattedDate = f.Invoke(dt); 

    /** 
    * The logic is not important here - what is important is that I 
    * am using the result of the executed lambda expression. 
    * */ 
    var builder = new StringBuilder(); 
    builder.Append(formattedDate); 
    builder.Append(" Hello!"); 
    return builder.ToString(); 
}; 

Girdinizin ifade var Geçirilen işlevinizi kullanan bir lambda (ifade) oluşturmak ve çevresinde ek bir çalışma yapmak. Yani aslında bu işlevi bir ifadenin içinde kullanmak istersiniz. Bu noktada

, beni bile ifadeleri kullanmayın önermek için izin verir. Sadece bir Func<DateTime, string> parametresini alan ve bunu işlemek için kullanan bir işlev oluşturabilirsiniz. Ancak, gerçekten bir şey için ifadeye ihtiyacınız varsa, nasıl bir tane inşa edeceğimi açıklamaya çalışacağım. Bu örnek için

, ben bu işlevi oluşturacaksınız: Gördüğünüz gibi, o zaman DateTime nesneyi oluşturur ve ardından fonksiyonu ve bunu geçiren bir Func<int, int, string> oluşturmak için gidiyorum

string MonthAndDayToString (int month, int day) 
{ 
    return "'" + formattingFunction(new DateTime(2013, month, day)) + "'" 
} 

sonucu daha da değiştirir.

Func<DateTime, string> formatting = dt => (...) // as above 

// define parameters for the lambda expression 
ParameterExpression monthParam = Expression.Parameter(typeof(int)); 
ParameterExpression dayParam = Expression.Parameter(typeof(int)); 

// look up DateTime constructor 
ConstructorInfo ci = typeof(DateTime).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int) }); 

// look up string.Concat 
MethodInfo concat = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string), typeof(string) }); 

// inner call: formatting(new DateTime(2013, month, day)) 
var call = Expression.Call(formatting.Method, Expression.New(ci, Expression.Constant(2013), monthParam, dayParam)); 

// concat: "'" + call + "'" 
var expr = Expression.Call(concat, Expression.Constant("'"), call, Expression.Constant("'")); 

// create the final lambda: (int, int) => expr 
var lambda = Expression.Lambda<Func<int, int, string>>(expr, new ParameterExpression[] { monthParam, dayParam }); 

// compile and execute 
Func<int, int, string> func = lambda.Compile(); 
Console.WriteLine(func(2, 1)); // '01.02.2013 Hello!' 
Console.WriteLine(func(11, 26)); // '26.11.2013 Hello!' 

Alex'in’cevabını baktıktan sonra ben sorunuzu yanlış ve ne yaptığınızı tersini çözmeye çalıştı gibi görünüyor. Ama aslında yapmaya çalıştıkları şeye bunu değiştirmek için hiç de farklı değil:

Func<DateTime, string> formatting = dt => dt.ToShortDateString(); 

ParameterExpression param = Expression.Parameter(typeof(DateTime)); 
MethodInfo concat = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string), typeof(string) }); 

var call = Expression.Call(formatting.Method, param); 
var expr = Expression.Call(concat, Expression.Constant("'"), call, Expression.Constant(" Hello!'")); 
var lambda = Expression.Lambda<Func<DateTime, string>>(expr, new ParameterExpression[] { param }); 

Func<DateTime, string> func = lambda.Compile(); 
Console.WriteLine(func(new DateTime(2013, 02, 01))); 
Console.WriteLine(func(new DateTime(2013, 11, 26))); 

Ama hala Func<DateTime, string> ve DateTime parametre alır normal bir fonksiyon korumak için çok daha kolay olacağını iddia ediyorum . Yani, 'un ifadeler gerçekten ifadelerine ihtiyaç duymadıkça, bunlardan kaçının.Seni hala düşünmüyorum Neden


gerçekten buna ifadeleri gerekir. Bu örneği ele alalım: Gördüğünüz gibi, bunu bir kez formatlama mantığı fonksiyonu inşa ediyorum

private Func<DateTime, string> formatting = dt => dt.ToShortDateString(); 
private Func<DateTime, string> formattingLogic = null; 

public Func<DateTime, string> FormattingLogic 
{ 
    get 
    { 
     if (formattingLogic == null) 
     { 
      // some results from reflection 
      string word = "Hello"; 
      string quote = "'"; 

      formattingLogic = dt => 
      { 
       StringBuilder str = new StringBuilder(quote); 
       str.Append(formatting(dt)); 

       if (!string.IsNullOrWhiteSpace(word)) 
        str.Append(" ").Append(word); 

       str.Append(quote); 
       return str.ToString(); 
      }; 
     } 

     return formattingLogic; 
    } 
} 

void Main() 
{ 
    Console.WriteLine(FormattingLogic(new DateTime(2013, 02, 01))); // '01.02.2013 Hello' 
    Console.WriteLine(FormattingLogic(new DateTime(2013, 11, 26))); // '26.11.2013 Hello' 
} 

, henüz set değil tembel zaman. Yansıma, işlevde bir yerlerde kullandığınız bazı değerleri almak için çalıştığında gerçekleşir. Fonksiyon bir lambda fonksiyonu olarak yaratıldıkça, lambda fonksiyonu içindeki yerel kapsamdan kullandığımız değişkenler otomatik olarak yakalanır ve kullanılabilir halde tutulur.

Elbette bunun yerine bir ifade olarak bunu oluşturabilir ve derlenmiş işlevi saklayabilirsiniz, ancak bunun böyle yapılmasının çok daha okunabilir ve sürdürülebilir olduğunu söyleyebilirim.

+0

Bu lambda, Expression.Block() 'gibi şeyler kullanılarak bir .Net 4.0 ifade ağacı olarak ifade edilebilir. Yine de bu ifadeyi doğrudan C# lambda kullanarak oluşturamayacaksınız. – svick

+0

@svick Evet, demek istediğim buydu. Benim cevabımdan çıktım bu kısmı gerçekten ilgimi çekmiyor zaten :) – poke

+1

Harika cevap - sadece sorduğum soru sende değil - sormadım soruyu cevapladım ama * gerçekten * cevabı bilmek istedim biçimlendirme mantığım için bir ifade yerine bir işlev). FYI - İfadelere ihtiyacımın sebebi, mantığın inşasının yansımaya dayanmasıdır - performans için derlenmiş işlevi önbelleğe almam gerekir. – Lawrence

1

Sen kullanabilirsiniz:

Expression<Func<DateTime, string>> formattingExpression = 
    dt => dt.ToShortDateString(); 

Ve sonuç: Yanlış anlamadıysam

var result = requiredLambda 
    .Invoke(formattingExpression.Compile(), new DateTime(2003, 2, 1)); 

// 1.2.2003 Hello! 
+1

Cevabınız için teşekkür ederiz. İdeal olarak, "requiredLambda" türünü "Func " olarak tutmak istiyorum, ancak cevabınız, etrafta bir yol bulamadığı takdirde kesinlikle yararlıdır. – Lawrence

İlgili konular