Как динамически установить свойство класса без использования отражения (с динамическим) в C # 4, когда имя свойства исходит из другого источника - PullRequest
6 голосов
/ 13 августа 2010

Я создаю / обновляю EntityFramework EntityObject во время выполнения. Я хочу установить свойства класса сущности, имена свойств и значения поступают из другого источника.

Итак, я делаю это;

    public static EntityCollection<T> UpdateLocaleEntity<T>(EntityCollection<T> entityCollectionToUpdate, params ILocaleControl[] values) where T : EntityObject
    {
        foreach (var x in entityCollectionToUpdate)
        {
            Type t = typeof(T);
            dynamic localeEntity = x;

            string cultureCode = localeEntity.CultureCode;

            for (int j = 0; j < values.Length; j++)
            {
                var value = values[j].GetLocaleValue(cultureCode);
                t.GetProperty(values[j].EntityPropertyName).SetValue(localeEntity, value, null);
            }
        }

        return entityCollectionToUpdate;
    }

Итак, как мне избавиться от "t.GetProperty (values ​​[j] .EntityPropertyName) .SetValue (localeEntity, value, null);" часть, есть ли динамический способ сделать это?

Что-то вроде;

dynamicCastedLocaleEntity.GetProperty (values ​​[j] .EntityPropertyName) = значение;

Спасибо.

Ответы [ 5 ]

13 голосов
/ 13 августа 2010

Длинный ответ подходит.Отражение прекрасно во многих ситуациях, ужасно в некоторых, но почти во всех случаях оно медленное.

Существует как минимум 4 различных способа установки свойства в .NET без использования отражения.

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

Деревья выражений были введены в .NET35 и используются для многих целей.,Здесь я использую их для построения выражения установщика свойства, а затем компилирую его в делегат.

В примере демонстрируется различное время для разных случаев, но вот мои числа: Контрольный случай (жестко запрограммированный): 0,02 с Отражение:Дерево выражений 1.78 с: 0.06 с

using System;
using System.Linq.Expressions;

namespace DifferentPropertSetterStrategies
{
   class TestClass
   {
      public string XY
      {
         get;
         set;
      }
   }

   class DelegateFactory
   {
      public static Action<object, object> GenerateSetPropertyActionForControl(
         )
      {
         return (inst, val) => ((TestClass) inst).XY = (string) val;
      }

      public static Action<object, object> GenerateSetPropertyActionWithReflection(
         Type type,
         string property
         )
      {
         var propertyInfo = type.GetProperty(property);

         return (inst, val) => propertyInfo.SetValue (inst, val, null);
      }

      public static Action<object,object> GenerateSetPropertyActionWithLinqExpression (
         Type type,
         string property
         )
      {
         var propertyInfo = type.GetProperty(property);
         var propertyType = propertyInfo.PropertyType;

         var instanceParameter = Expression.Parameter(typeof(object), "instance");
         var valueParameter = Expression.Parameter(typeof(object), "value");

         var lambda = Expression.Lambda<Action<object, object>> (
            Expression.Assign (
               Expression.Property (Expression.Convert (instanceParameter, type), propertyInfo),
               Expression.Convert(valueParameter, propertyType)),
            instanceParameter,
            valueParameter
            );

         return lambda.Compile();
      }
   }

   static class Program
   {
      static void Time (
         string tag, 
         object instance,
         object value,
         Action<object, object > action
         )
      {
         // Cold run
         action(instance, value);

         var then = DateTime.Now;
         const int Count = 2000000;
         for (var iter = 0; iter < Count; ++iter)
         {
            action (instance, value);
         }
         var diff = DateTime.Now - then;
         Console.WriteLine ("{0} {1} times - {2:0.00}s", tag, Count, diff.TotalSeconds);

      }

      static void Main(string[] args)
      {
         var instance = new TestClass ();
         var instanceType = instance.GetType ();

         const string TestProperty = "XY";
         const string TestValue = "Test";

         // Control case which just uses a hard coded delegate
         Time(
            "Control",
            instance,
            TestValue,
            DelegateFactory.GenerateSetPropertyActionForControl ()
            );

         Time(
            "Reflection", 
            instance, 
            TestValue, 
            DelegateFactory.GenerateSetPropertyActionWithReflection (instanceType, TestProperty)
            );

         Time(
            "Expression Trees", 
            instance, 
            TestValue, 
            DelegateFactory.GenerateSetPropertyActionWithLinqExpression(instanceType, TestProperty)
            );

         Console.ReadKey();
      }

   }
}
2 голосов
/ 14 марта 2011

Платформа с открытым исходным кодом ImpromptuInterface имеет методы для вызова на основе строки с использованием DLR, а не отражения, и работает быстрее, чем отражение.

Impromptu.InvokeSet(localeEntity, values[j].EntityPropertyName,value);
2 голосов
/ 13 августа 2010

возможно не с EntityObject, но если у вас был ExpandoObject, чем вы можете сделать

dynamic entity = new ExpandoObject();
(entity as IDictionary<String, Object>)[values[j].EntityPropertyName] = value
1 голос
/ 18 июля 2014

Что касается ответа FuleSnabel, вы можете значительно его ускорить (иногда в два раза быстрее в моих тестах).В некоторых тестах это было так же быстро, как решение Control:

public static Action<Object,Object> GenerateSetPropertyActionWithLinqExpression2(Type type, String property) {
    PropertyInfo pi = type.GetProperty(property,BindingFlags.Instance|BindingFlags.Public);
    MethodInfo mi = pi.GetSetMethod();
    Type propertyType = pi.PropertyType;

    var instance = Expression.Parameter(typeof(Object), "instance");
    var value = Expression.Parameter(typeof(Object), "value");

    var instance2 = Expression.Convert(instance, type);
    var value2 = Expression.Convert(value, pi.PropertyType);
    var callExpr = Expression.Call(instance2, mi, value2);

    return Expression.Lambda<Action<Object,Object>>(callExpr, instance, value).Compile();
}
1 голос
/ 13 августа 2010

Боюсь, что нет. Любое использование объекта dynamic запекается во время компиляции. Любой вызов, который может изменяться во время выполнения, должен выполняться с использованием отражения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...