Я пытаюсь улучшить производительность определенной части моей программы, которая включает в себя глубокое клонирование одного и того же графа объектов снова и снова в нескольких потоках. В настоящее время я использую сериализацию, которая является хорошей и простой реализацией, но мне бы хотелось чего-то более быстрого. Я столкнулся с идеей клонирования IL и пытаюсь работать с кодом, найденным здесь (блог Whizzo).
На самом деле я еще не понимаю IL, поэтому я надеюсь, что кто-то может немного помочь и объяснить мне некоторые вещи (я думаю, это первый вопрос из нескольких).
Вопрос здесь (и, кстати, если у кого-то есть хорошие ссылки, объясняющие коды операций и Reflection.emit немного больше, что было бы здорово, 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);
Я немного изменил код, так как мне всегда нужен глубокий клон, и я хочу, чтобы он основывался на сериализованных полях:
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);
}