views:

546

answers:

1

Dear ladies and sirs.

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.

+8  A: 

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"));
Yacoder
Indeed it is, although I did notice that question and even visited the MSDN article, but after reading the first paragraph in the Remarks section abandoned it. Just did not think of explicit interface implementation as giving another name to the interface method. Now I see my mistake. Thanks.
mark
That's why code review works! )
Yacoder