表达<FUNC <T>&GT;参数导致调用代码使用Expression.Lambda

时间:2017-02-08 07:10:13

标签: c# lambda expression-trees

我有一个泛型扩展方法,它接受一个Expression参数。该参数用于构建,编译和缓存表达式树。这一部分都运作良好(表达式只编译一次)。

但是当我分析应用程序时,我会在调用代码中看到在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);
        }
    }

1 个答案:

答案 0 :(得分: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);
    }

正如您所见,您的lambda是内联构造的。

不幸的是,对于一个被称为批量的方法,这会产生很大的性能影响。在这种情况下,要么将表达式树(例如x =&gt; x.PropertyX)保存到静态字段,要么如果它们太多,则按属性名称缓存它们(如果您的columntype数量有限,那么也是按列类型)