【问题标题】:set multiple properties simultaneously via reflection通过反射同时设置多个属性
【发布时间】:2010-07-10 09:36:13
【问题描述】:

我正在尝试优化代码中的反射利用率,但我想知道 是否可以一次设置对象的多个属性:

示例用法:

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 扩展方法替换为更有效的方法,但还没有找到合适的方法。 在此先感谢 ;)

我已经使用 cmets 中提供的链接修改了代码。常规方式仍然快 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);
        }
    }

【问题讨论】:

    标签: c# reflection plinq


    【解决方案1】:

    如果您进行了足够的反思,这确实是一个瓶颈,那么动态代码可能值得研究。在那之前——也许HyperDescriptor 会降低成本;非常相似的代码,但便宜得多。

    在 .NET 4.0 及更高版本中,Expression API 允许您设置多个属性,但这只有在您缓存委托时才真正可行。另一个有趣的选项(具有相同的约束:您必须缓存并重新使用委托)是DynamicMethod。但是,所有这些选项都是相当高级的主题。我很乐意为您提供建议,但您真的需要这个吗?

    【讨论】:

    • 感谢您的建议。我将探索这些主题。在某些情况下这是一个瓶颈,所以我真的在努力降低它的成本。
    • @Marc Gravell 在对添加到 HyperDescriptor 的类型执行 p.SetValue 时,有时可能会出现一些奇怪的错误
    • @Omu“有时可能会发生一些奇怪的错误”现在已成为我最喜欢的错误报告;p
    • @Marc Gravell 我很长时间没有使用它,但我记得,它是在执行需要隐式转换(拆箱)的 SetValue 时,就像你有一个类型的属性 p int 你做 p.SetValue(obj, x) 和 x 是对象 x = 1;比我得到某种溢出错误
    【解决方案2】:

    正如 Marc 已经告诉您的,最好使用动态代码来提高性能。

    您可以在 DynamicMethod 方法或 Delegate.CreateDelegate 与 GetSetMethod 之间进行选择。

    对于 DynamicMethod,您可以在此处查看示例:Late-Bound Invocations with DynamicMethod 至于 CreateDelegate 看看这个问题:CreateDelegate with unknown types

    【讨论】:

    • 感谢您提供有用的链接。但是,我还没有找到一种方法来将那里出现的对象开发到相应的列表中,并且性能改进微不足道......
    • @Gena 正如 Marc 所说,您需要添加缓存才能看到显着的性能改进。
    猜你喜欢
    • 2019-02-28
    • 1970-01-01
    • 2018-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-25
    • 2010-10-26
    • 1970-01-01
    相关资源
    最近更新 更多