2010-01-20 29 views
6

Çalışma zamanında lambda ifadelerini kullanan bir kod parçam vardı; bunları daha sonra derleyip çağırıyorum. yürütme zaman kazanmak içinBir lambda ifadesinde parametreleri değiştirme

Something thing; 

Expression<Action<Something>> expression = (c => c.DoWork()); 
Delegate del = expression.Compile(); 
del.DynamicInvoke(thing); 

, I, bir önbellekte anahtar lambda ifade dizisi olan bir Dictionary<String, Delegate> bu derlenmiş temsilci depolanan.

cache.Add("(Something)c => c.DoWork()", del); 

Aynı aramalar için, iyi çalıştı. Ancak, aynı delege kullanmam gereken "d => d.DoWork()" gibi eşdeğer lambdalar alabileceğimi fark ettim, ve ben değildim.

Bu bana temiz bir yol olup olmadığını merak ettim ("String.Replace kullanmadı", bunu geçici bir düzeltme olarak yaptım) öğelerini bir lambda ifadesinde değiştirmek için belki de arg0 ile değiştirerek Her iki

(c => c.DoWork()) ve

(d => d.DoWork()) dönüştürülmüş ve karşılaştırılır bir lambda bir Expression.Parameter (Tip, Ad) enjekte etmek fuctionnally benzer bir şey kullanarak (arg0 => arg0.DoWork()) olarak.

Bu mümkün mü? (Cevaplar C# 4.0 içerebilir)

+0

Anahtar her zaman şu şekilde olacaktır: (tür) arg => arg.Method() veya daha karmaşık biçimler bekleniyor? – Elisha

+0

Eh, alınan lambdas, (f, g) => f.Method (g) 'gibi daha fazla paramiye sahip olabilir, ve ben f ve g türleri olacaktır. Anahtar aslında oldukça sıradan ve taş olarak ayarlanmadı, 2 lambdas ('((T) arg0, (V) arg1) => arg0.Method (arg1)' nin yanında eşdeğerliği temsil etmenin herhangi bir uygun yolu hile yapabilirdi anahtar. –

cevap

3

Benim için en kolay yol olduğu için dizeleri kullandım. Parametre ifadesinin adını manuel olarak değiştiremezsiniz ("Ad" özelliğine sahiptir, ancak salt okunurdur), bu yüzden parçalardan yeni bir ifade oluşturmalısınız. Yaptığım şey bir "isimsiz" parametresi oluşturdu (aslında, bu durumda "Param_0" olan otomatik olarak oluşturulmuş bir ad alır) ve daha sonra yeni olanla neredeyse aynı olan yeni bir ifade yarattı, ancak yeni parametreyi kullanıyordu.

public static void Main() 
{ 
    String thing = "test"; 

    Expression<Action<String>> expression = c => c.ToUpper(); 
    Delegate del = expression.Compile(); 
    del.DynamicInvoke(thing); 

    Dictionary<String, Delegate> cache = new Dictionary<String, Delegate>(); 
    cache.Add(GenerateKey(expression), del); 

    Expression<Action<String>> expression1 = d => d.ToUpper(); 
    var test = cache.ContainsKey(GenerateKey(expression1)); 
    Console.WriteLine(test); 

} 

public static string GenerateKey(Expression<Action<String>> expr) 
{ 
    ParameterExpression newParam = Expression.Parameter(expr.Parameters[0].Type); 
    Expression newExprBody = Expression.Call(newParam, ((MethodCallExpression)expr.Body).Method); 
    Expression<Action<String>> newExpr = Expression.Lambda<Action<String>>(newExprBody, newParam); 
    return newExpr.ToString(); 
} 
+0

Sonunda benzer bir şeye geri düştüm :) Ama yine de lambda-ifadelerini karşılaştırmak için daha temsili bir yol arıyorum –

+0

@Alexandra Aynı vücut ifadesini kullanarak varolan bir MethodCallExpression'dan yeni bir MethodCallExpression oluşturmaya çalışıyorum ama farklı bir tür argüman ile. GenerateKey yönteminize benzer bir şey kullanarak bunu başarabilir misiniz? Bakınız: http://stackoverflow.com/questions/14363387/how-can-i-repurpose-a-generic-c-sharp-expression-allowing-the-input-type-argumen#comment19974655_14363387 –

3

Sadece metinden daha fazla bir lambda ifadesi var. Lambdalar derleyici tarafından çok daha karmaşık bir şeye yeniden yazılır. Örneğin, değişkenleri (diğer delegeleri içerebilecek şekilde) kapatabilirler. Bu, iki lambda'nın tam olarak aynı görünebileceği, ancak tamamen farklı eylemler gerçekleştirebileceği anlamına gelir.

Yani şans derlenmiş ifadeyi önbelleğe sahip olabilir, ancak ifadenin sadece metinden daha anahtarı için onlara daha anlamlı ad vermek gerekir. Aksi takdirde argüman ikamesi miktarı size yardımcı olmaz.

İlgili konular