2014-06-05 38 views
0

我想在C#中使用TypeBuilder來動態生成帶有函數的類,並讓該函數調用另一個基函數。C#TypeBuilder動態生成具有函數的類

需要這樣做的原因是,在Revit應用程序開發中,每個按鈕都需要有一個實現具有Execute功能的IExternalCommand的類。我想動態創建按鈕並根據它們的ID在運行時處理它們的執行,因此我需要動態創建類。

希望這個代碼得到跨越我正在尋找(或在此http://pastebin.com/eehGKteT):

using Autodesk.Revit.DB; 
using Autodesk.Revit.UI; 

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Reflection; 
using System.Reflection.Emit; 

namespace Centek_Revit_Addin 
{ 
    class DynamicButton 
    { 
     // I would like to use a function like this to generate the class during runtime, presumably using TypeBuilder: 
     public static void generateClass(int id) 
     { 
      // ... Code which would generate a class with the name "GeneratedClass" with the 'id' parameter appended at the end 
      // ... The class implements IExternalCommand 
      // ... The class has an Execute function with the parameters listed in the example, which returns a call to the Execute function in DynamicButton 
      //  along with the added integer 'id' parameter at the end 
     } 

     public static Autodesk.Revit.UI.Result Execute(ExternalCommandData revit, ref string message, ElementSet elements, int id) 
     { 
      TaskDialog.Show("About", "ID of the class that called us: " + id); 
      return Autodesk.Revit.UI.Result.Succeeded; 
     } 
    } 


    // ===== This class would have been generated during runtime using generateClass(15) ====== // 
    class GeneratedClass15 : Autodesk.Revit.UI.IExternalCommand 
    { 
     public Autodesk.Revit.UI.Result Execute(Autodesk.Revit.UI.ExternalCommandData revit, ref string message, Autodesk.Revit.DB.ElementSet elements) 
     { 
      return DynamicButton.Execute(revit, ref message, elements, 15); 
     } 
    } 
    // =================================================================== // 
} 

我試圖讓一個TypeBuilder工作,我想通了基礎知識,但我不能似乎數字瞭解如何使用Opcodes來獲得課程的學習方式。

所以基本上我正在尋找幫助編寫generateClass(int id)函數。任何幫助將非常感激!

編輯:

I would like to add my progress: 
using Autodesk.Revit.DB; 
using Autodesk.Revit.UI; 

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Reflection; 
using System.Reflection.Emit; 

namespace Centek_Revit_Addin 
{ 
    class DynamicButton 
    { 
     // I would like to use a function like this to generate the class during runtime, presumably using TypeBuilder: 
     public static void generateClass(int id) 
     { 
      // ... Code which would generate a class with the name "GeneratedClass" with the 'id' parameter appended at the end 
      // ... The class implements IExternalCommand 
      // ... The class has an Execute function with the parameters listed in the example, which returns a call to the Execute function in DynamicButton 
      //  along with the added integer 'id' parameter at the end 

      AssemblyName aName = new AssemblyName("DynamicAssemblyExample"); 
      AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndSave); 

      // For a single-module assembly, the module name is usually 
      // the assembly name plus an extension. 
      ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll"); 

      // Create class which extends Object and implements IExternalCommand 
      Type[] implements = {typeof(IExternalCommand)}; 
      TypeBuilder tb = mb.DefineType("GeneratedClass" + id, TypeAttributes.Public, typeof(Object), implements); 


      // Create 'Execute' function sig 
      Type[] paramList = {typeof(ExternalCommandData), typeof(string), typeof(ElementSet)}; 
      MethodBuilder mbExecute = tb.DefineMethod("Execute", MethodAttributes.Public, typeof(Result), paramList); 

      // Create 'Execute' function body 
      ILGenerator ilGen = mbExecute.GetILGenerator(); 

      ilGen.Emit(OpCodes.Nop); 
      ilGen.Emit(OpCodes.Ldarg_1); 
      ilGen.Emit(OpCodes.Ldarg_2); 
      ilGen.Emit(OpCodes.Ldarg_3); 

      ilGen.Emit(OpCodes.Ldc_I4_S, id); 

      Type[] paramListWID = { typeof(ExternalCommandData), typeof(string), typeof(ElementSet), typeof(int) }; 
      ilGen.EmitCall(OpCodes.Call, typeof(DynamicButton).GetMethod("Execute"), paramListWID); 


      //ilGen.Emit(OpCodes.Ret); 


      tb.CreateType(); 
     } 

     public static Autodesk.Revit.UI.Result Execute(ExternalCommandData revit, ref string message, ElementSet elements, int id) 
     { 
      TaskDialog.Show("About", "ID of the class that called us: " + id); 
      return Autodesk.Revit.UI.Result.Succeeded; 
     } 
    } 



    // ===== This class would have been generated during runtime using generateClass(15) ====== // 
    class GeneratedClass15 : Autodesk.Revit.UI.IExternalCommand 
    { 
     public Autodesk.Revit.UI.Result Execute(Autodesk.Revit.UI.ExternalCommandData revit, ref string message, Autodesk.Revit.DB.ElementSet elements) 
     { 
      return DynamicButton.Execute(revit, ref message, elements, 15); 
     } 
    } 
    // =================================================================== // 
} 

此代碼是非常接近,但在運行時,我得到的錯誤

System.TypeLoadException:式方法 '執行' 'GeneratedClass99' 從程序集「 DynamicAssemblyExample,Version = 0.0.0.0, Culture = neutral,PublicKeyToken = null'沒有實現。

這個錯誤,當我在generateClass(..)

回答

1

CreateType(..)首先你必須解決的參數,輸入您想使用發生。請注意,message參數具有ref屬性,因此您應該將typeof(String)更改爲Type.GetType("System.String&")

AFER,你必須說出你的執行方法實現(覆蓋)從接口execute方法:

tb.DefineMethodOverride(mbExecute, typeof(IExternalCommand).GetMethod("Execute")); 

我做了一些測試用consoleapplication並與變化上面我能得到它的工作:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Reflection; 
using System.Reflection.Emit; 

namespace ConsoleApplication10 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int a; 
      string s = ""; 
      while ((a = int.Parse(Console.ReadLine())) != 0) 
      { 


       var t = DynamicButton.generateClass(a); 

       ((IExternalCommand)t.GetConstructor(new Type[0]).Invoke(new object[0])).Execute(null, ref s, null); 
      } 
     } 
    } 

    public interface IExternalCommand 
    { 
     Result Execute(ExternalCommandData revit, ref string message, ElementSet elements); 
    } 

    public class DynamicButton 
    { 
     // I would like to use a function like this to generate the class during runtime, presumably using TypeBuilder: 
     public static Type generateClass(int id) 
     { 
      // ... Code which would generate a class with the name "GeneratedClass" with the 'id' parameter appended at the end 
      // ... The class implements IExternalCommand 
      // ... The class has an Execute function with the parameters listed in the example, which returns a call to the Execute function in DynamicButton 
      //  along with the added integer 'id' parameter at the end 

      AssemblyName aName = new AssemblyName("DynamicAssemblyExample"); 
      AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndSave); 

      // For a single-module assembly, the module name is usually 
      // the assembly name plus an extension. 
      ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll"); 

      // Create class which extends Object and implements IExternalCommand 
      var implements = new Type[] {typeof(IExternalCommand)}; 
      TypeBuilder tb = mb.DefineType("GeneratedClass" + id, TypeAttributes.Public, typeof(Object), implements); 


      // Create 'Execute' function sig 
      Type[] paramList = {typeof(ExternalCommandData), Type.GetType("System.String&"), typeof(ElementSet)}; 
      MethodBuilder mbExecute = tb.DefineMethod("Execute", MethodAttributes.Public | MethodAttributes.Virtual, typeof(Result), paramList); 

      // Create 'Execute' function body 
      ILGenerator ilGen = mbExecute.GetILGenerator(); 

      ilGen.Emit(OpCodes.Nop); 
      ilGen.Emit(OpCodes.Ldarg_1); 
      ilGen.Emit(OpCodes.Ldarg_2); 
      ilGen.Emit(OpCodes.Ldarg_3); 

      ilGen.Emit(OpCodes.Ldc_I4_S, id); 

      Type[] paramListWID = { typeof(ExternalCommandData), Type.GetType("System.String&"), typeof(ElementSet), typeof(int) }; 
      ilGen.EmitCall(OpCodes.Call, typeof(DynamicButton).GetMethod("Execute"), paramListWID); 


      ilGen.Emit(OpCodes.Ret); 



      tb.DefineMethodOverride(mbExecute, typeof(IExternalCommand).GetMethod("Execute")); 
      return tb.CreateType(); 
     } 

     public static Result Execute(ExternalCommandData revit, ref string message, ElementSet elements, int id) 
     { 
      Console.WriteLine("About {0}", "ID of the class that called us: " + id); 
      return Result.Succeeded; 
     } 
    } 

    public enum Result 
    { 
     Succeeded 
    } 

    public class ExternalCommandData { } 
    public class ElementSet { } 
    // =================================================================== // 
}