如何使用Reflection.Emit发出显式接口实现?

29

请观察以下简单的源代码:

using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;

namespace A
{
  public static class Program
  {
    private const MethodAttributes ExplicitImplementation =
      MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.Final |
      MethodAttributes.HideBySig | MethodAttributes.NewSlot;
    private const MethodAttributes ImplicitImplementation =
      MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig;

    private static Type EmitMyIntfType(ModuleBuilder moduleBuilder)
    {
      var typeBuilder = moduleBuilder.DefineType("IMyInterface",
        TypeAttributes.NotPublic | TypeAttributes.Interface | TypeAttributes.Abstract);
      typeBuilder.DefineMethod("MyMethod", MethodAttributes.Assembly | MethodAttributes.Abstract |
        MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot,
        typeof(void), new[] { typeof(int) });

      return typeBuilder.CreateType();
    }

    public static void Main()
    {
      var assemblyName = new AssemblyName("DynamicTypesAssembly");
      var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
      var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll", true);
      var myIntfType = EmitMyIntfType(moduleBuilder);

      var typeBuilder = moduleBuilder.DefineType("MyType",
        TypeAttributes.Public | TypeAttributes.BeforeFieldInit | TypeAttributes.Serializable |
        TypeAttributes.Sealed, typeof(object), new[] { myIntfType });

      //var myMethodImpl = typeBuilder.DefineMethod("IMyInterface.MyMethod", ExplicitImplementation,
      //  null, new[] { typeof(int) });
      var myMethodImpl = typeBuilder.DefineMethod("MyMethod", ImplicitImplementation,
        null, new[] { typeof(int) });
      var ilGenerator = myMethodImpl.GetILGenerator();
      ilGenerator.Emit(OpCodes.Ret);

      var type = typeBuilder.CreateType();
      assemblyBuilder.Save("A.dll");
    }
  }
}

以下是使用Reflector反编译A.dll程序集获得的等效C#代码:

internal interface IMyInterface
{
    void MyMethod(int);
}
[Serializable]
public sealed class MyType : IMyInterface
{
    public override void MyMethod(int)
    {
    }
}

现在如果我希望 MyType 类显式实现 IMyInterface 接口怎么办呢? 我可以采用以下代码:

//var myMethodImpl = typeBuilder.DefineMethod("IMyInterface.MyMethod", ExplicitImplementation,
//  null, new[] { typeof(int) });
var myMethodImpl = typeBuilder.DefineMethod("MyMethod", ImplicitImplementation,
  null, new[] { typeof(int) });

并且切换注释以产生以下代码:

var myMethodImpl = typeBuilder.DefineMethod("IMyInterface.MyMethod", ExplicitImplementation,
  null, new[] { typeof(int) });
// var myMethodImpl = typeBuilder.DefineMethod("MyMethod", ImplicitImplementation,
//  null, new[] { typeof(int) });

但是现在,应用程序无法创建动态类型。这行代码:

var type = typeBuilder.CreateType();

抛出以下异常:

System.TypeLoadException was unhandled
  Message="Method 'MyMethod' in type 'MyType' from assembly 'DynamicTypesAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation."
  Source="mscorlib"
  TypeName="MyType"
  StackTrace:
       at System.Reflection.Emit.TypeBuilder._TermCreateClass(Int32 handle, Module module)
       at System.Reflection.Emit.TypeBuilder.TermCreateClass(Int32 handle, Module module)
       at System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
       at System.Reflection.Emit.TypeBuilder.CreateType()
       at A.Program.Main() in C:\Home\work\A\Program.cs:line 45
  InnerException: 

有谁能够展示给我我的代码哪里出了问题吗?

谢谢。

1个回答

35

这个问题看起来和这个问题很类似...

它指向MSDN的文档:

然而,如果您要提供I.M()的单独实现,则必须定义方法体,然后使用DefineMethodOverride方法将该方法体与表示I.M()的MethodInfo关联起来。 方法体名称并不重要。

        // Build the method body for the explicit interface 
        // implementation. The name used for the method body 
        // can be anything. Here, it is the name of the method,
        // qualified by the interface name.
        //
        MethodBuilder mbIM = tb.DefineMethod("I.M", 
            MethodAttributes.Private | MethodAttributes.HideBySig |
                MethodAttributes.NewSlot | MethodAttributes.Virtual | 
                MethodAttributes.Final,
            null,
            Type.EmptyTypes);
        ILGenerator il = mbIM.GetILGenerator();
        il.Emit(OpCodes.Ldstr, "The I.M implementation of C");
        il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", 
            new Type[] { typeof(string) }));
        il.Emit(OpCodes.Ret);

        // DefineMethodOverride is used to associate the method 
        // body with the interface method that is being implemented.
        //
        tb.DefineMethodOverride(mbIM, typeof(I).GetMethod("M"));

确实如此,虽然我注意到了这个问题,甚至访问了MSDN文章,但在阅读备注部分的第一段之后就放弃了。我并没有想到显式接口实现可以将接口方法命名为另一个名称。现在我看到了我的错误。谢谢。 - mark

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接