【发布时间】:2011-05-21 07:24:13
【问题描述】:
使用 Mono.Cecil 我想重写以下属性:
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 bool [mscorlib]System.Object::Equals(object, object)
但它被写成:
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 都失败了。
我正在使用主分支 https://github.com/jbevain/cecil 构建 Mono.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 指令之一。
-
我可以发誓我之前在 Reflector 中看到了 2 条 ldarg_0 指令 :-)。谢谢。
标签: c# mono.cecil