Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to emit explicit interface implementation using reflection.emit?

Observe the following simple source code:

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");     }   } } 

Below is the equivalent C# code obtained by decompiling the A.dll assembly using the Reflector:

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

Now what if I wish the MyType type implement the IMyInterface interface explicitly? So I take these lines:

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

and switch the comments to yield this code:

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

But now, the application fails to create the dynamic type. This line:

var type = typeBuilder.CreateType(); 

throws the following exception:

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:  

Can anyone show me what is wrong with my code?

Thanks.

like image 627
mark Avatar asked Nov 30 '09 19:11

mark


1 Answers

That seems duplicate to this question...

Which points to MSDN:

However, to provide a separate implementation of I.M(), you must define a method body and then use the DefineMethodOverride method to associate that method body with a MethodInfo representing I.M(). The name of the method body does not matter.

        // 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")); 
like image 83
Max Galkin Avatar answered Sep 20 '22 02:09

Max Galkin