Отражение Относительно Сколько я жертвую скоростью? - PullRequest
3 голосов
/ 20 апреля 2009
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 ]

3 голосов
/ 20 апреля 2009

Вы могли бы повысить производительность, попробовав что-то с помощью интерфейса INotifyPropertyChanged. Вместо отражения вы можете использовать моделирование на основе событий, чтобы выполнить то же самое.

CSLA.NET является примером структуры, которая использует этот подход.

Пример

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

и тогда OnPropertyChanged будет выглядеть примерно так:

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

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

3 голосов
/ 20 апреля 2009

Вы задаете 2 вопроса:

  1. Какую скорость вы теряете?
  2. Есть ли более быстрый способ сделать это

Вопрос № 1:

Ответ на первый вопрос: это зависит. Написание кода проверки свойства вручную может быть в несколько раз быстрее, чем код отражения. Однако это не может быть проблемой в зависимости от того, как часто вызывается код. Если код вызывается не очень часто, вы не получите много за его оптимизацию. Однако, если это называется много, то оптимизация может дать вам большие улучшения скорости. Я бы запустил ваше приложение под профилировщиком (мне лично нравится Dot Trace от Jet Brain), чтобы увидеть, где на самом деле тратится время. Процент времени, проведенного внутри «GenerateDirtyPropertiesOnEntity», даст вам теоретический максимальный прирост производительности, который вы можете получить, оптимизируя метод. Если это окажется небольшим процентом, я просто оставлю код как есть.

Вопрос № 2

Я могу придумать 2 простых способа сделать это быстрее:

  1. Введите код сравнения свойств от руки.
  2. Используйте класс DynamicMethod для генерации кода сравнения

Я предполагаю, что вы не хотите делать # 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 голос
/ 20 апреля 2009

Единственный способ узнать, без сомнения, сколько скорости вы жертвуете, - это профилировать это.

В целом, по моему опыту, размышления о свойствах, по-видимому, в лучшем случае примерно 1/50 скорости доступа к ним напрямую. В худшем случае это может быть в 200 раз медленнее. В зависимости от частоты этой операции и количества свойств, это может быть, а может и не быть заметной разницей, хотя, опять же, поэтому я бы рекомендовал профилировать его, чтобы узнать, нужно ли вам другое решение.

1 голос
/ 20 апреля 2009

Я собираюсь сделать нечто подобное, используя PostSharp , затем сравнивая старое и новое значение свойства, когда оно устанавливается, и помечая объект как грязный. Не должно быть слишком сложно делать то же самое на уровне свойств.

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