C # Reflection IL - Понимание того, как копируются значения - PullRequest
0 голосов
/ 16 марта 2012

Я пытаюсь повысить производительность определенной части моей программы, которая включает в себя глубокое клонирование одного и того же графа объектов снова и снова в нескольких потоках.В настоящее время я использую сериализацию, которая является хорошей легкой реализацией, но я хотел бы что-то быстрее.Я наткнулся на идею клонирования IL и пытаюсь поработать с кодом, найденным здесь (блог Whizzo) .

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

Вопрос здесь (и, между прочим, если у кого-то есть хорошие ссылки, объясняющие коды операций и рефлексию. Допустите немного больше, что было бы здорово, MSDN не дает много деталей), как копируются значения?Я вижу, что новый объект создается и извлекается из стека

generator.Emit(OpCodes.Newobj, cInfo);
generator.Emit(OpCodes.Stloc, cloneVariable);

Затем немного позже, учитывая интересующее значение поля, значение каким-то образом копируется.Я не понимаю, как мы возвращаемся к исходному объекту и получаем его значение, когда на исходный объект, похоже, нет ссылки?Или это какая-то магия LocalBuilder (я не уверен на 100%, что он делает):

// I *THINK* this Pushes the cloneVariable on the stack, loads an argument (from where?) and sets the field value based on the FieldInfo??
generator.Emit(OpCodes.Ldloc, cloneVariable);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, field);
generator.Emit(OpCodes.Stfld, field);

Я немного изменил код, так как всегда хочу клон Deep и хочу, чтобы он основывался насериализованные поля:

private static T CloneObjectWithILDeep(T myObject)
{
   Delegate myExec = null;
   if (!_cachedILDeep.TryGetValue(typeof(T), out myExec))
   {
     // Create ILGenerator            
     DynamicMethod dymMethod = new DynamicMethod("DoDeepClone", typeof(T), new Type[] { typeof(T) }, Assembly.GetExecutingAssembly().ManifestModule, true);
     ILGenerator generator = dymMethod.GetILGenerator();
     LocalBuilder cloneVariable = generator.DeclareLocal(myObject.GetType());

     ConstructorInfo cInfo = myObject.GetType().GetConstructor(Type.EmptyTypes);
     generator.Emit(OpCodes.Newobj, cInfo);
     generator.Emit(OpCodes.Stloc, cloneVariable);

     foreach (FieldInfo field in typeof(T).GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public))
     {         
        if(field.IsNotSerialized)
            continue;

        if (field.FieldType.IsValueType || field.FieldType == typeof(string))
        {
           generator.Emit(OpCodes.Ldloc, cloneVariable);
           generator.Emit(OpCodes.Ldarg_0);
           generator.Emit(OpCodes.Ldfld, field);
           generator.Emit(OpCodes.Stfld, field);
         }
         else if (field.FieldType.IsClass)
         {
           CopyReferenceType(generator, cloneVariable, field);
         }
       }

      generator.Emit(OpCodes.Ldloc_0);
      generator.Emit(OpCodes.Ret);
      myExec = dymMethod.CreateDelegate(typeof(Func<T, T>));
      _cachedILDeep.Add(typeof(T), myExec);
    }
    return ((Func<T, T>)myExec)(myObject);
  }

Ответы [ 2 ]

3 голосов
/ 17 марта 2012

Сначала у вас есть

generator.Emit(OpCodes.Ldloc, cloneVariable);
generator.Emit(OpCodes.Ldarg_0);

Ваш стек содержит (cloneVariable, myObject)

enerator.Emit(OpCodes.Ldfld, field);

Этот объект извлекает ссылку на объект, получает значение из поля и помещает его в стек

Ваш стек содержит (cloneVariable, myObject.field)

generator.Emit(OpCodes.Stfld, field);

Этот объект извлекает ссылку на объект и его значение и сохраняет значение в поле объекта. Результат эквивалентен C #

cloneVariable.field = myObject.field
0 голосов
/ 27 октября 2015

on't Не будет ли проще использовать структуры вместо классов и напрямую помещать байтовый массив типа в память нового объекта?

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

[Edit] Забудьте об указателях, вы можете сделать это небезопасно, просто выполнив маршалинг.Проверьте этот пост;

Как преобразовать структуру в байтовый массив в C #?

[Edit2] Мне нужно научиться терпению: P ваше решение гораздо быстрее.

...