首页 > 解决方案 > Mono.Cecil AddInterfaceImplementation 等效?

问题描述

我正在研究 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 等效项或解决方法以及如何使用它?

标签: reflectionmonocode-generationmono.cecil

解决方案


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

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

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

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# 的更多信息:

  1. Sharplab.io
  2. Cecilifier(免责声明:我是这个的作者)

话虽如此,如果您可以使用 C# 9.0,您也许可以利用新的Source Generators 功能


推荐阅读