Когда компилируется выражение <T>, оно неявно кэшируется?

Когда скомпилирован Expression<T>, результирующий код неявно кэшируется каркасом? Я думаю о строках статических методов Regex, где структура неявно компилирует и кэширует последние несколько регулярных выражений.

Если скомпилированные Expression<T> объекты являются not кэшированными, можете ли вы рекомендовать некоторые рекомендации по сохранению времени компиляции или любых ошибок, которые могут вызвать проблемы, если я вручную кэширую выражение?

public MyResultType DoSomething(int arg1, int arg2)
{
    var result = invokeHandler(
        (IDoSomethingHandler h) => h.DoSomething(arg1, arg2)
    );
    return result;
}

private TResult invokeHandler<T, TResult>(Expression<Func<T, TResult>> action)
    where T : class
{
    // Here, I might want to check to see if action is already cached.

    var compiledAction = action.Compile();
    var methodCallExpr = action as MethodCallExpression;

    // Here, I might want to store methodCallExpr in a cache somewhere.

    var handler = ServiceLocator.Current.GetInstance<T>();
    var result = compiledAction(handler);

    return result;
}

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

Ответы

Ответ 1

Нет; Я не верю, что это так; если вы хотите его кешировать, вы должны придерживаться ссылки Delegate (обычно Func<...> или Action<...>). Аналогично, если вы хотите получить лучшую производительность, вы должны скомпилировать его как параметризованное выражение, поэтому при его вызове вы можете отправлять разные значения.

В этом случае повторная фразировка поможет:

public MyResultType DoSomething(int arg1, int arg2)
{
    var result = invokeHandler(
        (IDoSomethingHandler h, int a1, int a2) => h.DoSomething(a1, a2),
        arg1, arg2);
    return result;
}

private TResult invokeHandler<T, TResult>(Expression<Func<T,int,int,TResult>> action,
    int arg1, int arg2)
    where T : class
{
    // Here, I might want to check to see if action is already cached.

    var compiledAction = action.Compile();
    var methodCallExpr = action as MethodCallExpression;

    // Here, I might want to store methodCallExpr in a cache somewhere.

    var handler = ServiceLocator.Current.GetInstance<T>();
    var result = compiledAction(handler, arg1, arg2);

    return result;
}

то есть. введите параметры номера выражения и передайте фактические значения во время выполнения (а не как константы в выражении).

Ответ 2

Lambda experssions не кэшируются автоматически. Для этого вам понадобится реализовать собственные алгоритмы кэширования/замещения. Проверьте связанный с этим вопрос Stackoverflow:

Возможно ли кэшировать значение, оцениваемое в выражении лямбда?

Важно отметить, что лямбда-выражения ленивы оцениваются в С#.