编译表达式比反射慢

Dev*_*ion -1 c# performance expression-trees

我有一个具有动态集的PropertyInfo.SetValue.意味着要设置的值是未知的.

我从互联网上得到了这样的方法.

private static Action<object, object> CreateSetAccess(MethodInfo method)
{
    var obj = Expression.Parameter(typeof(object), "o");
    var value = Expression.Parameter(typeof(object));

    Expression<Action<object, object>> expr =
        Expression.Lambda<Action<object, object>>(
            Expression.Call(
                Expression.Convert(obj, method.DeclaringType),
                method,
                Expression.Convert(value, method.GetParameters()[0].ParameterType)),
            obj,
            value);

    return expr.Compile();
}
Run Code Online (Sandbox Code Playgroud)

这样做是创建一个表达式并对其进行编译,但是使用参数类型转换对象.

我像这样消费它.

var method2 = CreateSetAccess(property.GetSetMethod());
method2(response, valueToSet);
Run Code Online (Sandbox Code Playgroud)

发生的事情似乎是慢了 PropertyInfo.SetValue

这是我的基准

var xpathNavigator = XmlHelper.CreateXPathDocument(serviceResponse).CreateNavigator();
foreach (var propertyInformation in propertyInformationSource)
{
    // Gets the node using the NodePath provided in the Attribute
    var attr = propertyInformation.Value;
    var pathValue = xpathNavigator.SelectSingleNode(attr.NodePath);
    if (pathValue == null)
        continue;

    object valueToSet = null;
    var property = propertyInformation.Key;

    if (propertyInformation.Value.ShouldDeserialize)
        valueToSet = serializationHelper.Deserialize(property.PropertyType, pathValue.OuterXml, attr.CustomRoot);
    else
        valueToSet = Convert.ChangeType(pathValue.Value, property.PropertyType);

    // this line is only added for the testing for it to be JITd
    var method = CreateSetAccess(property.GetSetMethod());
    method(response, valueToSet);
    property.SetValue(response, valueToSet);
    // end

    TimeSpan fastSet, setValue;

    const int COUNT = 100000;
    var watch = Stopwatch.StartNew();
    for (int i = 0; i < COUNT; i++)
    {
        var method2 = CreateSetAccess(property.GetSetMethod());
        method2(response, valueToSet);
    }
    watch.Stop();

    fastSet = watch.Elapsed; // result {00:00:08.8500760}

    watch = Stopwatch.StartNew();
    for (int i = 0; i < COUNT; i++)
    {
        property.SetValue(response, valueToSet);
    }
    watch.Stop();

    setValue = watch.Elapsed; // result {00:00:00.0263953}
}
Run Code Online (Sandbox Code Playgroud)

我想知道为什么会这样?我猜是因为我总是在创建一个新的表达式,但我怎么能让它创建一个新的对象并让它被缓存?

usr*_*usr 5

如果每次编译一个新表达式都比较快,那么Reflection API就会在内部执行此操作.因此,事实并非如此.只有在多次重用相同的编译代码时,此方法才有效.

所以没有办法根据提供的methodinfo在运行时调整表达式?

反思就是这样做的,这就是让它如此缓慢的原因.保留已编译方法的缓存.例如,在Dictionary<MethodInfo, Action<object, object>>具有合适的比较器的情况下.