c# - 使用 Mono.Cecil 将方法的主体替换为另一个方法的主体?

标签 c# reflection cil reflection.emit mono.cecil

Mono.Cecil当我们只需设置 Body 时,看起来很简单目标MethodDefinitionBody来源MethodDefinition 。对于简单的方法来说,效果还不错。但对于某些使用自定义类型的方法(例如初始化一个新对象),它将不起作用(在写回程序集时会抛出异常)。

这是我的代码:

//in current app
public class Form1 {
  public string Test(){
   return "Modified Test";
  }
}
//in another assembly
public class Target {
  public string Test(){
    return "Test";
  }
}

//the copying code, this works for the above pair of methods
//the context here is of course in the current app
var targetAsm = AssemblyDefinition.ReadAssembly("target_path");
var mr1 = targetAsm.MainModule.Import(typeof(Form1).GetMethod("Test"));
var targetType = targetAsm.MainModule.Types.FirstOrDefault(e => e.Name == "Target");
var m2 = targetType.Methods.FirstOrDefault(e => e.Name == "Test");
var m1 = mr1.Resolve();
var m1IL = m1.Body.GetILProcessor();

foreach(var i in m1.Body.Instructions.ToList()){
   var ci = i;
   if(i.Operand is MethodReference){
      var mref = i.Operand as MethodReference;
      ci = m1IL.Create(i.OpCode, targetType.Module.Import(mref));
   }
   else if(i.Operand is TypeReference){
      var tref = i.Operand as TypeReference;
      ci = m1IL.Create(i.OpCode, targetType.Module.Import(tref));
   }
   if(ci != i){
       m1IL.Replace(i, ci);
   }
}
//here the source Body should have its Instructions set imported fine
//so we just need to set its Body to the target's Body
m2.Body = m1.Body;
//finally write to another output assembly
targetAsm.Write("modified_target_path");

上面的代码没有从任何地方引用,我只是自己尝试了一下,发现它适用于简单的情况(例如我上面发布的两种方法Test)。但是如果源方法(在当前应用程序中定义)包含一些类型引用(例如某些构造函数 init ...),如下所示:

public class Form1 {
  public string Test(){
   var u = new Uri("SomeUri");
   return u.AbsolutePath;
  }
}

那么在写回程序集时就会失败。抛出的异常是 ArgumentException带有以下消息:

"Member 'System.Uri' is declared in another module and needs to be imported"

事实上,我之前遇到过类似的消息,但它是针对像 ( string.Concat ) 这样的方法调用的。这就是为什么我尝试导入 MethodReference (您可以在我发布的代码中的 if 循环中看到 foreach )。这确实适用于这种情况。

但是这种情况不同,我不知道如何正确导入使用/引用的类型(在这种情况下是 System.Uri )。据我所知 Import 的结果应该使用 MethodReference可以看到结果被用来替换Operand对于每个 Instruction 。但对于本例中的类型引用,我完全不知道如何进行。

最佳答案

我在问题中发布的所有代码都很好,但还不够。实际上是异常消息:

"Member 'System.Uri' is declared in another module and needs to be imported"

提示 VariableDefinitionVariableType。我只导入指令,但不导入变量(变量只是从源 MethodBody 中准确引用的)。因此,解决方案是我们也需要以相同的方式导入变量(也许还​​导入 ExceptionHandlers ,因为 ExceptionHandler 具有 CatchType )应该导入)。 下面是导入 VariableDefinition 的类似代码:

var vars = m1.Body.Variables.ToList();
m1.Body.Variables.Clear();
foreach(var v in vars){
   var nv = new VariableDefinition(v.Name, targetType.Module.Import(v.VariableType));
   m1.Body.Variables.Add(nv);
}

关于c# - 使用 Mono.Cecil 将方法的主体替换为另一个方法的主体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46052447/

相关文章:

C# - 获取程序集的所有 'usings'

cil - 用于从字节数组中获取 IL 的 API

c# - == 运算符到底做了什么?

c# - HTTP 415 无法处理消息,因为内容类型 'application/json; charset=utf-8' 不是预期的类型 'text/xml; charset=utf-8'

c# - 添加到现有索引模板的映射(使用 NEST 属性)

C#正则表达式匹配字符串

.net - C# 中 get 前缀背后的原因

C#。使用反射设置成员对象值

c# - 处理双 NaN 和 Inf 时的 ILASM 问题

c# - 如何在 C# 中将 cookie 过期时间设置为 "session"?