2017-02-08 8 views
1

У меня есть общий метод расширения, который принимает параметр выражения. Параметр используется для сборки, компиляции и кэширования дерева выражений. Эта часть работает хорошо (выражение компилируется только один раз).Выражение <Func<T>> параметр вызывает вызов кода для использования Expression.Lambda

Но когда я просматриваю приложение, я вижу время, проведенное в Expression.Lambda и Expression.Property в , вызывающем код. Является ли это ожидаемым или я сделал что-то неправильно в определении/компиляции моих выражений?

Вот упрощенная версия кода участвуют:

namespace Example 
{ 
    public class Caller 
    { 
     public void SetPropertyX(MyTypedDataRow dataRow, string value) 
     { 
      // Inside this method, the profiler shows time spent in Expression.Lambda and Expression.Property 
      // even when the same property is reused and the compiled expression stored in the dictionary of 
      // DataRowExpressionHelper is used. 
      dataRow.Set(x => x.PropertyX, value); 
     } 
    } 

    public static class DataRowExtensions 
    { 
     public static void Set<TRow, TValue>(this TRow row, Expression<Func<TRow, TValue>> property, TValue value) where TRow : DataRow 
     { 
      DataRowExpressionHelper<TRow>.Set(row, property, value); 
     } 
    } 

    internal static class DataRowExpressionHelper<TRow> where TRow : DataRow 
    { 
     private static Dictionary<string, object> _propertySetters = new Dictionary<string, object>(); 

     internal static void Set<TValue>(TRow row, Expression<Func<TRow, TValue>> property, TValue value) 
     { 
      var propertyInfo = (PropertyInfo) ((MemberExpression) property.Body).Member; 
      var propertyName = propertyInfo.Name; 

      Action<TRow, TValue> propertySetter; 

      object untypedSetter; 
      if (_propertySetters != null && _propertySetters.TryGetValue(propertyName, out untypedSetter)) 
      { 
       propertySetter = (Action<TRow, TValue>)untypedSetter; 
      } 
      else 
      { 
       var targetRow = Expression.Parameter(typeof(TRow), "targetRow"); 
       var newValue = Expression.Parameter(typeof(TValue), "newValue"); 

       propertySetter = Expression.Lambda<Action<TRow, TValue>>(
        Expression.Block(/* Logic here */), 
        // Input parameters 
        targetRow, 
        newValue 
       ).Compile(); 

       var updatedPropertySetters = new Dictionary<string, object>(_propertySetters); 
       updatedPropertySetters[propertyName] = propertySetter; 
       _propertySetters = updatedPropertySetters; 
      } 

      propertySetter.Invoke(row, value); 
     } 
    } 
+0

Код будет использовать ветвь компиляции при первом вызове имени свойства, поэтому по меньшей мере один вызов будет отображаться в профилировщике с момента его первого вызова. –

+0

@ScottChamberlain, я думаю, вы, возможно, ударили ноготь по голове там. Я, конечно же, ожидал, что для компиляции будут вызовы Expression, но я ожидал их внутри метода DataRowExpressionHelper.Set, где происходит .Compile(). Но когда я смотрю сейчас, я не вижу никаких вызовов выражения. * Внутри DataRowExpressionHelper.Set, поэтому кажется, что вместо этого появляется вызов в коде вызова. –

+0

См. [Рекомендации по оптимизации передаваемых выражений в качестве параметров метода] (http://stackoverflow.com/questions/41847754/suggestions-for-optimizing-passing-expressions-as-method-parameters) –

ответ

0

Если вы посмотрите на декомпилируемой код вызывающего абонента, вы увидите что-то вроде следующего:

public void SetPropertyX(MyTypedDataRow dataRow, string value) 
    { 
     var parameter = Expression.Parameter("x", typeof(MyTypedDataRow)); 
     dataRow.Set(Expression.Lambda<Func<MyTypedDataRow, string>>(Expression.Property(parameter, "PropertyX"), parameter) , value); 
    } 

Как вы можете см. ваш лямбда построена в линию.

К сожалению, для метода, который называется много, это имеет большое влияние на производительность. В этом случае либо сохраните дерево выражений (например, x => x.PropertyX) в статическом поле, либо их слишком много, затем кешируйте их по имени свойства (и если у вас ограниченное количество типов столбцов, по типам столбцов)