Mono.Cecil AddInterfaceImplementation 等效?

Posted

技术标签:

【中文标题】Mono.Cecil AddInterfaceImplementation 等效?【英文标题】:Mono.Cecil AddInterfaceImplementation equivalent? 【发布时间】:2021-10-09 13:59:36 【问题描述】:

我正在开发 Mono.Cecil codegen util,我想执行以下操作:

Loop through types
If type contains X attribute:
- Add ITestInterface implementation (where ITestInterface has defined some methods)
// For reference

public interface ITestInterface

    void Something();
    int IntSomething();


// Expected result, if type contains X attribute:
// Before codegen:
[X]
public class CodeGenExample



// After codegen
[X]
public class CodeGenExample : ITestInterface

   public void Something()
   
       // some stuff
   

   public int IntSomething()
   
       // do some stuff
       return 0;
   


我看到 .NET Reflection 有一个 AddInterfaceImplementation 方法 (https://docs.microsoft.com/pl-pl/dotnet/api/system.reflection.emit.typebuilder.addinterfaceimplementation?view=net-5.0)。

是否有 Mono.Cecil 等效项或解决方法以及如何使用它?

【问题讨论】:

【参考方案1】:

这可以通过以下方式实现:

    迭代程序集中定义的所有类型 检查哪些类型应用了该属性 注入方法。

例如,您可以执行以下操作:

using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace inject

    interface IMyInterface 
    
        int Something();
    

    class MarkerAttribute : Attribute 

    [Marker]
    class Foo
    
    

    class Program
    
        static void Main(string[] args)
        
            if (args.Length == 1)
            
                using var a = AssemblyDefinition.ReadAssembly(typeof(Program).Assembly.Location);
                var interfaceToImplement = a.MainModule.GetType("inject.IMyInterface");

                foreach(var t in a.MainModule.Types)
                
                    if (t.HasCustomAttributes && t.CustomAttributes.Any(c => c.Constructor.DeclaringType.Name == "MarkerAttribute"))
                    
                        System.Console.WriteLine($"Adding methods to : t");
                        
                        var something = new MethodDefinition("Something", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, a.MainModule.TypeSystem.Int32);
                        something.Body = new Mono.Cecil.Cil.MethodBody(something);
                        var il = something.Body.GetILProcessor();

                        il.Emit(OpCodes.Ldc_I4, 42);
                        il.Emit(OpCodes.Ret);
                        t.Methods.Add(something);


                        // Add the interface.
                        t.Interfaces.Add(new InterfaceImplementation(interfaceToImplement));

                        var path  = typeof(Program).Assembly.Location + ".new";
                        a.Write(path);

                        System.Console.WriteLine($"Modified version written to path");
                    
                
            
            else
                       
                object f = new Foo();
                IMyInterface itf = (IMyInterface) f;
                System.Console.WriteLine($"Something() == itf.Something()");
            
        
    

另一个可能的解决方案是在内部类中实现方法并复制它们的方法体。

附带说明一下,您可以使用以下 2 个在线工具来探索/了解有关 CIL、Mono.Cecil、C# 的更多信息:

    Sharplab.io Cecilifier(免责声明:我是这篇文章的作者)

也就是说,如果您可以使用 C# 9.0,您或许可以利用新的 Source Generators feature。

【讨论】:

附加问题,这只添加了 Something() 方法。但是我需要一个 ITestInterface 实现,因为我想在 codegen 之后将该类传递给例如 StaticTestInvoker(ITestInterface test)。 对不起.. 完全忽略了接口实现位。我已经更新了答案以包含它。

以上是关于Mono.Cecil AddInterfaceImplementation 等效?的主要内容,如果未能解决你的问题,请参考以下文章

Mono.Cecil AddInterfaceImplementation 等效?

使用MOno Cecil 的相关开源项目

使用 Mono.Cecil 在 C# 程序集中注入方法

Mono.Cecil 替换方法中的参数

使用Mono Cecil对MSIL进行注入

mono.cecil.dll是啥文件