2010-07-10 2 views
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); 
     } 
    } 

ответ

4

Если вы делаете достаточно отражения, что это действительно узким местом, то динамический код может быть стоит исследовать. До тех пор - возможно, HyperDescriptor уменьшит стоимость; очень похожий код, но гораздо дешевле.

В .NET 4.0 и выше API Expression позволяет устанавливать несколько свойств, но это действительно реально, если вы кешируете делегат. Другой интересный вариант (с тем же ограничением: вы должны кэшировать и повторно использовать делегат) - DynamicMethod. Однако все эти варианты - довольно продвинутые темы. Я рад дать вам совет, но вы действительно нужно это?

+0

спасибо за советы. Я собираюсь изучить эти темы. В некоторых случаях это узкое место, поэтому я действительно стараюсь уменьшить его стоимость. –

+0

@Marc Gravell при выполнении p.SetValue по типу, который добавлен в HyperDescriptor, иногда могут возникать некоторые странные ошибки – Omu

+1

@Omu «иногда могут возникнуть некоторые странные ошибки» теперь стала гордостью места как мой любимый отчет об ошибках всех time: p –

1

Поскольку Марк уже сказал вам, что лучше использовать динамический код для повышения производительности.

Вы можете выбрать между DynamicMethod или Delegate.CreateDelegate с помощью GetSetMethod.

Для DynamicMethod вы можете увидеть пример здесь: Late-Bound Invocations with DynamicMethod Что касается CreateDelegate взглянуть на этот вопрос: CreateDelegate with unknown types

+0

Спасибо за полезные ссылки. Однако я не нашел способ разработать объекты, представленные там, в корреспондентских списках, и улучшение производительности было незначительным ... –

+0

@ Gena, поскольку Марк сказал, что вам нужно добавить кеширование, чтобы увидеть значительное улучшение производительности. – Giorgi

+0

Как я могу достичь такого? Никогда не делал этого раньше ... –

Смежные вопросы