Замена параметров в лямбда-выражении

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

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)


person Dynami Le Savard    schedule 20.01.2010    source источник
comment
Ключ всегда будет в формате: (type)arg => arg.Method() или ожидаются более сложные форматы?   -  person Elisha    schedule 20.01.2010
comment
Ну, полученные лямбды могли бы иметь больше параметров, например (f,g) => f.Method(g), а у меня будут типы f и g. На самом деле ключ довольно приземлен и не высечен на камне, любой подходящий способ представить эквивалентность между двумя лямбда-выражениями кроме ((T)arg0,(V)arg1) => arg0.Method(arg1) может помочь в качестве ключа.   -  person Dynami Le Savard    schedule 20.01.2010


Ответы (2)


Я использовал строки, так как это был самый простой способ для меня. Вы не можете вручную изменить имя выражения параметра (у него есть свойство «Имя», но оно доступно только для чтения), поэтому вы должны составить новое выражение из кусочков. Что я сделал, так это создал «безымянный» параметр (на самом деле, в данном случае он получает автоматически сгенерированное имя, то есть «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();
}
person Alexandra Rusina    schedule 20.01.2010
comment
В конце концов я вернулся к чему-то похожему :) Но я все еще ищу более репрезентативный способ сравнения лямбда-выражений. - person Dynami Le Savard; 03.02.2010
comment
@Alexandra Александра Я пытаюсь создать новое выражение MethodCallExpression из существующего выражения MethodCallExpression, используя то же выражение тела, но с аргументом другого типа. Можно ли сделать это, используя что-то похожее на ваш метод GenerateKey? См.: stackoverflow.com/questions/14363387/ - person Vinney Kelly; 16.01.2013

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

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

person Joel Coehoorn    schedule 20.01.2010