2010-01-20 14 views
6

Tenía una parte del código que toma expresiones lambda en tiempo de ejecución, que luego puedo compilar e invocar.Reemplazar parámetros en una expresión lambda

Something thing; 

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

Con el fin de ahorrar tiempo de ejecución, guardé los delegados compilados en una memoria caché, una Dictionary<String, Delegate> el que la clave es la cadena de expresión lambda.

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

Para exactamente las mismas llamadas, funcionó bien. Sin embargo, me di cuenta de que podía recibir lambdas equivalentes, como "d => d.DoWork()", que en realidad debería usar el mismo delegado, y no lo era.

Esto me hizo preguntarme si había una manera limpia (leer "no usar String.Replace", ya lo hice como una solución temporal) para reemplazar los elementos en una expresión lambda, como quizás reemplazarlos por arg0 para que tanto

(c => c.DoWork()) y (d => d.DoWork())

se transforman y se comparó como (arg0 => arg0.DoWork()) mediante el uso de algo fuctionnally similar a la inyección de un Expression.Parameter (Tipo, Nombre) en una lambda.

¿Es esto posible? (Las respuestas pueden incluir C# 4.0)

+0

¿La clave siempre tendrá el formato: (tipo) arg => arg.Method() o se esperan formatos más complejos? – Elisha

+0

Bueno, las lambdas recibidas podrían tener más params, como '(f, g) => f.Método (g)', y tendré los tipos de f y g. La clave es bastante mundana en realidad y no está escrita en piedra, cualquier forma adecuada para representar la equivalencia entre 2 lambdas junto a '((T) arg0, (V) arg1) => arg0.Método (arg1)' podría hacer el truco como la clave. –

Respuesta

3

Utilicé cadenas, ya que era la forma más fácil para mí. No puede cambiar manualmente el nombre de la expresión del parámetro (tiene la propiedad "Nombre", pero es de solo lectura), por lo que debe construir una nueva expresión a partir de piezas. Lo que hice fue crear un parámetro "sin nombre" (de hecho, obtiene un nombre autogenerado en este caso, que es "Param_0") y luego creé una nueva expresión casi igual a la anterior, pero usando el nuevo parámetro.

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

Al final volví a algo similar :) Pero sigo buscando una forma más representativa de comparar lambda-expressions –

+0

@Alexandra Estoy intentando construir una nueva MethodCallExpression desde una MethodCallExpression existente usando la misma expresión corporal pero con un argumento de tipo diferente. ¿Podría uno lograr esto usando algo similar a su método GenerateKey? Ver: http://stackoverflow.com/questions/14363387/how-can-i-repurpose-a-generic-c-sharp-expression-allowing-the-input-type-argumen#comment19974655_14363387 –

3

Hay más en una expresión lambda que solo el texto. Lambdas son reescritos por el compilador en algo mucho más complicado. Por ejemplo, pueden cerrar sobre variables (que podrían incluir otros delegados). Esto significa que dos lambdas podrían verse exactamente iguales, pero realizar acciones completamente diferentes.

Por lo tanto, puede tener suerte en el almacenamiento en caché de su expresión compilada, pero debe darles un nombre significativo para la clave que solo el texto de la expresión. De lo contrario, ninguna cantidad de sustitución de argumentos te ayudará.

Cuestiones relacionadas