Замена параметров в лямбда-выражении - PullRequest
6 голосов
/ 20 января 2010

У меня была часть кода, которая принимает лямбда-выражения во время выполнения, которые затем я могу скомпилировать и вызвать.

Something thing;

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

Чтобы сэкономить время выполнения, я сохранил эти скомпилированные делегаты в кеше, Dictionary<String, Delegate>, ключом которого является строка лямбда-выражения.

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

Для точно таких же звонков все работало нормально. Однако я понял, что могу получать эквивалентные лямбды, такие как "d => d.DoWork ()", для которых мне действительно нужно использовать тот же делегат, а я не был.

Это заставило меня задуматься, есть ли чистый способ (читай "не используя String.Replace", я уже сделал это как временное исправление) для замены элементов в лямбда-выражении, например, возможно, заменив их на arg0 что оба

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

преобразуются и сравниваются как (arg0 => arg0.DoWork()) с помощью чего-то похожего на введение параметра Expression.Parameter (Type, Name) в лямбду.

Это возможно? (Ответы могут включать C # 4.0)

Ответы [ 2 ]

3 голосов
/ 20 января 2010

Я использовал струны, так как это был самый простой способ для меня. Вы не можете вручную изменить имя выражения параметра (оно имеет свойство «Имя», но оно доступно только для чтения), поэтому вы должны создать новое выражение из частей. Я создал параметр «безымянный» (на самом деле он получает автоматически сгенерированное имя, в данном случае «Param_0»), а затем создал новое выражение, почти такое же, как старое, но с использованием нового параметра.

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();
}
3 голосов
/ 20 января 2010

В лямбда-выражении есть нечто большее, чем просто текст. Лямбды переписываются компилятором в нечто гораздо более сложное. Например, они могут закрывать переменные (которые могут включать других делегатов). Это означает, что две лямбды могут выглядеть одинаково, но выполнять совершенно разные действия.

Так что вам может повезти, если вы кешируете свое скомпилированное выражение, но вам нужно дать им более значимое имя ключа, чем просто текст выражения. В противном случае никакая подстановка аргументов вам не поможет.

...