通过反射同时设置多个属性

3
我将尝试优化代码中的反射利用率,并想知道是否可以一次设置对象的多个属性:
示例用法:
private void SetProperties<T>(List<T> objects, List<Tuple<string, object>> propsAndValues) where T:Task
        {
            Type type = typeof(T);
            var propInfos = propsAndValues.ToDictionary(key => type.GetProperty(key.Item1, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.SetProperty), elem => elem.Item2);

            objects.AsParallel().ForAll(obj =>
                {
                    obj.SetProps(propInfos);                                  
                });

        }

在哪里

public static void SetProps<T>(this T obj, Dictionary<PropertyInfo, object> propInfos) where T : Task
        {
            foreach (var propInfo in propInfos)
            {
                propInfo.Key.SetValue(obj, propInfo.Value, null);
            }            
        }

我希望能够用更高效的方法替换SetProps扩展方法,但目前还没有找到合适的替代方法。 提前感谢;)

我已经使用评论中提供的链接修改了代码。然而,常规方法仍然比这种方式快4倍。问题是这是否已经达到极限,或者还有改进的空间?

class DelegateFactory
{
    public delegate void LateBoundPropertySet(object target, object value);

    public delegate void LateBoundPropertyListSet(object target, List<object> values);

    public static LateBoundPropertySet CreateSetIL(PropertyInfo property)
    {
        var method = new DynamicMethod("Set" + property.Name, null, new[] { typeof(object), typeof(object) }, true);
        var gen = method.GetILGenerator();

        var sourceType = property.DeclaringType;
        var setter = property.GetSetMethod(true);

        gen.Emit(OpCodes.Ldarg_0); // Load input to stack
        gen.Emit(OpCodes.Castclass, sourceType); // Cast to source type
        gen.Emit(OpCodes.Ldarg_1); // Load value to stack
        gen.Emit(OpCodes.Unbox_Any, property.PropertyType); // Unbox the value to its proper value type
        gen.Emit(OpCodes.Callvirt, setter); // Call the setter method
        gen.Emit(OpCodes.Ret);

        var result = (LateBoundPropertySet)method.CreateDelegate(typeof(LateBoundPropertySet));

        return result;
    }

    public static LateBoundPropertySet CreateSet(PropertyInfo property)
    {

        var setterType = typeof(Action<,>).MakeGenericType(property.DeclaringType, property.PropertyType);

        var propertyWriter = typeof(PropertyWriter<,>).MakeGenericType(property.DeclaringType, property.PropertyType);

        var setterDelegate = Delegate.CreateDelegate(setterType, property.GetSetMethod());

        var writer = (IPropertyWriter)Activator.CreateInstance(propertyWriter, setterDelegate);

        return writer.SetValue;

    }

    private interface IPropertyWriter
    {
        void SetValue(object instance, object value);
    }

    private interface IPropertyListWriter
    {

        void SetValues(object instance, List<object> values);

    }

    private class PropertyWriter<TInstance, TProperty> : IPropertyWriter
    {

        private readonly Action<TInstance, TProperty> _setValueDelegate;

        public PropertyWriter(Action<TInstance, TProperty> setValueDelegate)
        {

            _setValueDelegate = setValueDelegate;

        }

        public void SetValue(object instance, object value)
        {

            _setValueDelegate((TInstance)instance, (TProperty)value);
        }

    }

}

public static void SetProps2<T>(this T obj, Dictionary<DelegateFactory.LateBoundPropertySet, object> propSetters) where T : Task
    {                        
        foreach (var propSet in propSetters)
        {
            propSet.Key.Invoke(propSet, propSet.Value);
        }
    }
2个回答

4
如果你已经反思了足够的时间,确认这真正是一个瓶颈,那么动态代码可能值得探究。在此之前,也许HyperDescriptor可以降低成本;它的代码非常相似,但更便宜。
在 .NET 4.0 及以上版本中,Expression API 允许你设置多个属性,但只有在缓存委托时才真正可行。另一个有趣的选项(带有同样的限制:必须缓存和重用委托)是 DynamicMethod。然而,所有这些选项都是相当高级的话题。我很乐意提供建议,但你真的需要吗?

谢谢建议。 我将要探索这些话题。 在某些情况下它是瓶颈,所以我真的在努力降低它的成本。 - Gena Verdel
@Marc Gravell 当在添加到HyperDescriptor的类型上执行p.SetValue时,有时可能会出现一些奇怪的错误。 - Omu
1
@Omu:“有时候会出现一些奇怪的错误”现在已经成为我有史以来最喜欢的错误报告了;p - Marc Gravell
@Marc Gravell 我已经很久没有使用它了,但是我记得当执行需要隐式转换(拆箱)的SetValue操作时,例如您有一个类型为int的属性p并且您执行p.SetValue(obj, x),其中x是对象x = 1; 那么我会收到某种溢出错误。 - Omu

1

感谢提供有用的链接。 然而,我还没有找到一种方法将那里介绍的对象开发成相应的列表,并且性能改进微不足道... - Gena Verdel
@Gena,正如Marc所说,您需要添加缓存以实现显著的性能提升。 - Giorgi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接