【问题标题】:How to dynamically set a property of a class without using reflection (with dynamic) in C# 4 when property name is coming from another source当属性名称来自另一个来源时,如何在 C# 4 中动态设置类的属性而不使用反射(动态)
【发布时间】:2011-03-29 08:38:43
【问题描述】:

我正在运行时构建/更新 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) = value;

谢谢。

【问题讨论】:

  • 如果真有dynamicCastedLocaleEntity.GetProperty(values[j].EntityPropertyName) = value;这样的表达式,运行时怎么知道你要给哪个实例赋值?

标签: reflection dynamic c#-4.0 entity-framework-4


【解决方案1】:

长答案即将到来。 反射在许多情况下都很棒,在某些情况下很糟糕,但在几乎所有情况下它都很慢。

至少有 4 种不同的方法可以在 .NET 中设置属性而无需使用反射。

我以为我演示了其中之一:使用编译的表达式树。请注意,表达式构建也相当昂贵,因此将使用它构建的委托缓存在字典中非常重要(例如):

表达式树是在 .NET35 中引入的,用于许多事情。这里我使用它们来构建一个属性设置器表达式,然后将它编译成一个委托。

该示例演示了针对不同情况的不同时间安排,但这里是我的数字: 控制案例(硬编码):0.02s 反射:1.78s 表达式树:0.06s

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();
      }

   }
}

【讨论】:

  • 以下是 4 种不同的方式(我知道)如何设置不带反射的属性: 1. Delegate.Create 2. DynamicMethod 3. 表达式树 4. CallSite.Create
  • 我在下面添加了一个更快的表达式树代码sn-p。
【解决方案2】:

对于 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();
}

【讨论】:

  • 你在哪里传递属性的值!我是表达式的新手,我只是找不到查看值的部分,因为在前面的示例中他正在通过变量“val”传递值
  • Action&lt;Object,Object&gt; callSetName = GenerateSetPropertyActionWithLinqExpression2(typeof(Person), "FirstName"); 然后Person p = new Person(); callSetName(p, "Bob");
  • @Loathing 这实际上可能适用于我想要做的事情。我需要一个完全通用的解决方案来根据字符串列表设置属性值。 ..thx..
【解决方案3】:

可能不使用 EntityObject,但如果你有一个 ExpandoObject,那你就做不到

dynamic entity = new ExpandoObject();
(entity as IDictionary<String, Object>)[values[j].EntityPropertyName] = value

【讨论】:

    【解决方案4】:

    开源框架 ImpromptuInterface 具有基于字符串调用的方法,使用 DLR 而不是反射,并且运行速度也比反射快。

    Impromptu.InvokeSet(localeEntity, values[j].EntityPropertyName,value);
    

    【讨论】:

      【解决方案5】:

      恐怕不会。 dynamic 对象的任何使用都在编译时被烘焙。任何可能在运行时发生变化的调用都必须使用反射来完成。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-05-22
        • 2021-12-30
        • 2011-04-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-10
        • 1970-01-01
        相关资源
        最近更新 更多