Mono.Cecil 사용하기Mono.Cecil을 사용하여 System.Object.Equals에 호출을 주입하는 방법?
public string FirstName
{
get { return _FirstName; }
set
{
_FirstName = value;
}
}
다음으로 바꾸기 :
public string FirstName
{
get { return _FirstName; }
set
{
if (System.Object.Equals(_FirstName, value))
{
return;
}
_FirstName = value;
}
}
이것은 재 작성 내용의 일부분이지만, 문제.
리플렉터 사용 다음 코드는 System.Object.Equals() 호출을 제외하고 필요한대로 속성을 다시 작성합니다. IL 코드가 다음과 같을 경우 :
하지만 다음과 같이 작성됩니다.
call instance void RewriteSharp.Person::.ctor()
System.Object.Equals에 대한 호출을 작성하는 코드는 다음과 같습니다.
setMethodWriter.InsertBefore(
firstExistingInstruction,
setMethodWriter.Create(OpCodes.Call, objectEqualsMethodReference));
ObjectEqualsMethodReference를 초기화하는 데 사용되는 메소드는 다음과 같습니다.
private static MethodReference GetSystemObjectEqualsMethodReference(
AssemblyDefinition assembly
)
{
var typeReference = assembly.MainModule.GetTypeReferences()
.Single(t => t.FullName == "System.Object");
var typeDefinition = typeReference.Resolve();
var methodDefinition = typeDefinition.Methods.Single(
m => m.Name == "Equals"
&& m.Parameters.Count == 2
&& m.Parameters[0].ParameterType.Name == "Object"
&& m.Parameters[1].ParameterType.Name == "Object"
);
return methodDefinition;
}
setMethodWriter.Create() 또는 GetSystemObjectEqualsMethodReference()가 잘못되어 디버깅이 필요하지 않은 것으로 보입니다.
작성된 등록 정보와 등록 정보를 다시 쓰는 코드는 동일한 프레임 워크 목표를가집니다. 3.5 및 4.0 모두 실패합니다.
Mono.Cecil 빌드 master 분기 https://github.com/jbevain/cecil 사용하고 있습니다.
전체 코드 목록
using Mono.Cecil;
using Mono.Cecil.Cil;
using System;
using System.Linq;
namespace RewriteNotifyPropertyChanged
{
class Program
{
static void Main(string[] args)
{
var rewrite = "..\\RewriteSharp.dll";
var rewritten = "..\\RewritenSharp.dll";
var typeName = "Person";
var propertyName = "FirstName";
var assembly = AssemblyDefinition.ReadAssembly(rewrite);
var typeDefinition = assembly.MainModule.Types.Single(t => t.Name == typeName);
var propertyDefintion = typeDefinition.Properties
.Single(p => p.Name == propertyName);
var setMethodWriter = propertyDefintion.SetMethod.Body.GetILProcessor();
var backingFieldReference = GetBackingFieldReference(typeDefinition, propertyName);
var objectEqualsMethodReference = GetSystemObjectEqualsMethodReference(assembly);
var firstExistingInstruction = setMethodWriter.Body.Instructions[0];
setMethodWriter.InsertBefore(
firstExistingInstruction,
setMethodWriter.Create(OpCodes.Ldarg_0));
setMethodWriter.InsertBefore(
firstExistingInstruction,
setMethodWriter.Create(OpCodes.Ldfld, backingFieldReference));
setMethodWriter.InsertBefore(
firstExistingInstruction,
setMethodWriter.Create(OpCodes.Ldarg_1));
setMethodWriter.InsertBefore(
firstExistingInstruction,
setMethodWriter.Create(OpCodes.Call, objectEqualsMethodReference));
setMethodWriter.InsertBefore(
firstExistingInstruction,
setMethodWriter.Create(OpCodes.Brfalse_S, firstExistingInstruction));
setMethodWriter.InsertBefore(
firstExistingInstruction,
setMethodWriter.Create(OpCodes.Ret));
assembly.Write(rewritten, new WriterParameters { WriteSymbols = true });
Console.WriteLine("Done.");
Console.ReadKey();
}
private static MethodReference GetSystemObjectEqualsMethodReference(
AssemblyDefinition assembly
)
{
var typeReference = assembly.MainModule.GetTypeReferences()
.Single(t => t.FullName == "System.Object");
var typeDefinition = typeReference.Resolve();
var methodDefinition = typeDefinition.Methods.Single(
m => m.Name == "Equals"
&& m.Parameters.Count == 2
&& m.Parameters[0].ParameterType.Name == "Object"
&& m.Parameters[1].ParameterType.Name == "Object"
);
return methodDefinition;
}
private static FieldReference GetBackingFieldReference(
TypeDefinition typeDefinition,
string propertyName
)
{
var fieldName = "_" + propertyName;
var fieldReference = typeDefinition.Fields.Single(f => f.Name == fieldName);
return fieldReference;
}
}
}
이것은 당신이 설명하는 문제를 해결하지 않지만 그럼에도 불구하고 문제 - 당신은 ldarg_0 지침 중 하나를 꺼내해야합니다. – cdhowie
나는 Reflector에서 2 ldarg_0 명령을 전에 봤다고 맹세 할 수있다 :-). 감사. –