反思相关我牺牲了多少速度?

时间:2009-04-19 23:07:54

标签: c# reflection performance

private Equipment GenerateDirtyPropertiesOnEntity(Equipment updatedEntity)
    {
        updatedEntity.DirtyProperties.Clear();
        Equipment originalEntity = GetEquipmentByGuid(updatedEnitity.Guid.Value);
        Type myType = updatedEntity.GetType();
        System.Reflection.PropertyInfo[] properties = myType.GetProperties();
        foreach (System.Reflection.PropertyInfo p in properties)
        {
            if (p.GetValue(originalEntity, null) == null)
            {
                if (p.GetValue(updatedEntity, null) != null)
                    updatedEntity.DirtyProperties.Add(p.Name);
            }
            else
            {
                if (!(p.GetValue(originalEntity, null).Equals(p.GetValue(updatedEntity, null))))
                    updatedEntity.DirtyProperties.Add(p.Name);
            }
        }
        return updatedEntity;
    }

使用它时我牺牲多少速度? 有谁知道更好的方法吗?

提前致谢

4 个答案:

答案 0 :(得分:3)

你问了两个问题:

  1. 你失去了多少速度?
  2. 有更快的方法吗
  3. 问题#1:

    第一个答案是:它取决于。手动编写属性检查代码可能比反射代码快几倍。但是,根据调用代码的频率,这可能实际上不是问题。如果代码没有经常调用,那么你就不会因为优化代码而烦恼。但是,如果它被称为很多,那么优化它可能会带来很大的速度提升。我会在探查器下面运行你的应用程序(我个人喜欢Jet Brain的Dot Trace),看看实际花费的时间。在“GenerateDirtyPropertiesOnEntity”中花费的时间百分比将为您提供通过优化方法可以获得的理论最大性能增益。如果最终只占很小的百分比,那么我只是按原样保留代码。

    问题#2

    我可以想到两种简单的方法来加快速度:

    1. 手动编写属性比较代码。
    2. 使用DynamicMethod类生成比较代码
    3. 我假设你不想做#1。我会在一秒钟内发布一些显示#2的代码。

      <强>更新

      以下是生成动态方法的代码

      class Util
      {
          public static Func<T,T, List<string>> CreateDitryChecker<T>()
          {
              var dm = 
                  new DynamicMethod
                  (
                      "$dirty_checker", 
                      typeof(List<string>), 
                      new[] { typeof(T), typeof(T) }, 
                      typeof(T)
                  );
      
              var ilGen = dm.GetILGenerator();
      
              //var retVar = new List<string>();
              var retVar = ilGen.DeclareLocal(typeof(List<string>));
              ilGen.Emit(OpCodes.Newobj, typeof(List<string>).GetConstructor(new Type[0]));
              ilGen.Emit(OpCodes.Stloc, retVar);
      
              var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
      
              MethodInfo objEqualsMehtod = typeof(object).GetMethod("Equals", new[] { typeof(object) });
              MethodInfo listAddMethod = typeof(List<string>).GetMethod("Add");
      
              foreach (PropertyInfo prop in properties)
              {
                  //Inject code equivalent to the following into the method:
      
                  //if (arg1.prop == null)
                  //{
                  //     if (arg2.prop != null)
                  //     {
                  //         retVar.Add("prop")
                  //     }
                  //}
                  //else
                  //{
                  //    if (! arg1.prop.Equals(arg2))
                  //    {
                  //        retVar.Add("prop")    
                  //    }
                  //}
                  Label endLabel = ilGen.DefineLabel();
                  Label elseLabel = ilGen.DefineLabel();
      
                  //if arg1.prop != null, goto elseLabel
                  ilGen.Emit(OpCodes.Ldarg_0);
                  ilGen.Emit(OpCodes.Call, prop.GetGetMethod());
                  ilGen.Emit(OpCodes.Brtrue, elseLabel);
      
                  //if arg2.prop != null, goto endLabel
                  ilGen.Emit(OpCodes.Ldarg_1);
                  ilGen.EmitCall(OpCodes.Call, prop.GetGetMethod(), null);
                  ilGen.Emit(OpCodes.Brfalse, endLabel);
      
                  //retVar.Add("prop");
                  ilGen.Emit(OpCodes.Ldloc, retVar);
                  ilGen.Emit(OpCodes.Ldstr, prop.Name);
                  ilGen.EmitCall(OpCodes.Callvirt, listAddMethod, null);
                  ilGen.Emit(OpCodes.Br, endLabel);
      
                  //elseLabel:
                  ilGen.MarkLabel(elseLabel);
      
                  //if (arg0.prop.Equals(arg1.prop), goto endLabel
                  ilGen.Emit(OpCodes.Ldarg_0);
                  ilGen.EmitCall(OpCodes.Call, prop.GetGetMethod(), null);
                  ilGen.Emit(OpCodes.Ldarg_1);
                  ilGen.EmitCall(OpCodes.Call, prop.GetGetMethod(), null);
                  ilGen.EmitCall(OpCodes.Callvirt, objEqualsMehtod, null);
                  ilGen.Emit(OpCodes.Brtrue, endLabel);
      
                  //retVar.Add("prop")
                  ilGen.Emit(OpCodes.Ldloc, retVar);
                  ilGen.Emit(OpCodes.Ldstr, prop.Name);
                  ilGen.EmitCall(OpCodes.Callvirt, listAddMethod, null);
      
                  //endLAbel:
                  ilGen.MarkLabel(endLabel);
              }
      
              ilGen.Emit(OpCodes.Ldloc, retVar);
              ilGen.Emit(OpCodes.Ret);
      
      
              return (Func<T, T, List<string>>) dm.CreateDelegate(typeof(Func<T, T, List<string>>));
          }
      }
      

      它接受一个泛型参数T并返回一个委托,当给定2个T实例时,它将返回所有已更改属性的列表。

      为了提高性能,最好只调用一次方法并将结果存储在只读静态字段中。像这样的东西会起作用:

      class FooBar
      {
         static readonly Func<FooBar,FooBar, List<string>> s_dirtyChecker;
      
         static FooBar()
         {
             s_dirtyChecker = Util.CreateDirtyChecker<FooBar>();
         }
      
         public List<string> GetDirtyProperties(Foobar other)
         {
             return s_dirtyChecker(this, other);
         }
      }
      

答案 1 :(得分:3)

可能 通过使用INotifyPropertyChanged界面尝试来获得更好的性能。您可以使用基于事件的建模来完成同样的事情,而不是使用反射。

CSLA.NET是采用该方法的框架示例。

示例

T SomeProperty()
{
    get
    {
       return _someProperty;
    }
    set
    {
       if (_someProperty <> value)
       {
          _someProperty = value;
          OnPropertyChanged("SomeProperty");
       }
    }
}

然后OnPropertyChanged看起来像

OnPropertyChanged(object params)
{
   DirtyProperties.Add(params);
}

请记住这是全部空气代码。我不记得params是如何构造的,但它实际上并不是object类型,并且包含了属性的名称,这就是你如何确定要添加到DirtyProperties列表中的属性。

答案 2 :(得分:1)

我正在使用PostSharp做类似的事情,然后在设置时比较旧属性值和新属性值,并将对象标记为脏。在物业层面做同样的事情不应该太难。

答案 3 :(得分:1)

毫无疑问,要知道你牺牲多少速度的唯一方法就是分析这个。

总的来说,根据我的经验,反映属性似乎最多只是直接访问它们的速度的1/50。在最坏的情况下,它可能慢200倍。根据此操作的频率和属性的数量,这可能是也可能不是显着的差异,但这也是我建议分析它以告诉您是否需要不同解决方案的原因。