2009-11-30 12 views
23

Observe le code source simple:Comment émettre une implémentation d'interface explicite à l'aide de reflection.emit?

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

Ci-dessous est l'équivalent du code C# obtenu par décompilation l'ensemble A.dll utilisant le réflecteur:

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

Maintenant, si je souhaite le type MyType mettre en œuvre l'interface IMyInterface explicitement? Alors je prends ces lignes:

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

et commuter les commentaires pour obtenir ce code:

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

Mais maintenant, l'application ne parvient pas à créer le type dynamique. Cette ligne:

var type = typeBuilder.CreateType(); 

lance l'exception suivante:

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: 

Quelqu'un peut-il me montrer ce qui ne va pas avec mon code?

Merci.

Répondre

28

Cela semble en double pour this question ...

qui points to MSDN:

Cependant, pour fournir une mise en œuvre séparée de la messagerie instantanée(), vous devez définir un corps de méthode, puis utiliser le DefineMethodOverride méthode à associer ce corps de méthode avec un MethodInfo représentant IM(). Le nom du corps de la méthode n'est pas .

 // 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")); 
+0

Il est en effet, bien que je l'ai fait remarquer à cette question et même visité l'article MSDN, mais après avoir lu le premier paragraphe de la section Remarques abandonnais. Je ne pensais pas à une implémentation d'interface explicite en donnant un autre nom à la méthode d'interface. Maintenant, je vois mon erreur. Merci. – mark

+0

Voilà pourquoi la révision de code fonctionne!) –