【问题标题】:Mono.Cecil AddInterfaceImplementation equivalent?Mono.Cecil AddInterfaceImplementation 等效?
【发布时间】: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 等效项或解决方法以及如何使用它?

【问题讨论】:

    标签: reflection mono code-generation mono.cecil


    【解决方案1】:

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

    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 feature

    【讨论】:

    • 附加问题,这只添加了 Something() 方法。但是我需要一个 ITestInterface 实现,因为我想在 codegen 之后将该类传递给例如 StaticTestInvoker(ITestInterface test)。
    • 对不起.. 完全忽略了接口实现位。我已经更新了答案以包含它。
    猜你喜欢
    • 2011-08-06
    • 1970-01-01
    • 1970-01-01
    • 2011-11-08
    • 1970-01-01
    • 2011-06-22
    • 2012-09-27
    • 1970-01-01
    • 2013-06-28
    相关资源
    最近更新 更多