加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 如何使用Mono.Cecil注入System.Object.Equals?

发布时间:2020-12-16 01:39:42 所属栏目:百科 来源:网络整理
导读:使用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; } _Fir
使用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;
    }
}

这只是重写的内容,但它是我遇到问题的地方.

使用Reflector我可以看到以下代码根据需要重写属性,除了调用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都失败了.

我正在使用master分支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,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;
}
}
}

解决方法

Cecil与System.Reflection不同,它区分了引用和定义,并且每个模块都有一个范围.这意味着您不能简单地使用自己内部另一个模块的MethodDefinition.你必须创建一个适当的引用.这是塞西尔术语中称为 importing的过程.

具体来说,GetSystemObjectEqualsMethodReference返回corlib中定义的方法,您需要在模块中创建对它的引用:

更换:

var objectEqualsMethodReference = GetSystemObjectEqualsMethodReference(assembly);

通过:

var objectEqualsMethodReference = assembly.MainModule.Import (GetSystemObjectEqualsMethodReference(assembly));

修复IL应该可以使它工作.

此外,虽然我在,它的方法:

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 MethodReference GetSystemObjectEqualsMethodReference(AssemblyDefinition assembly)
{
    var @object = assembly.MainModule.TypeSystem.Object.Resolve ();

    return @object.Methods.Single(
        m => m.Name == "Equals"
            && m.Parameters.Count == 2
            && m.Parameters[0].ParameterType.MetadataType == MetadataType.Object
            && m.Parameters[1].ParameterType.MetadataType == MetadataType.Object);
}

assembly.Write(rewritten,new WriterParameters { WriteSymbols = true });

如果在阅读程序集时未传递新的ReaderParameters {ReadSymbols = true},则没有多大意义.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读