首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C# TypeBuilder动态生成类

C# TypeBuilder动态生成类
EN

Stack Overflow用户
提问于 2014-06-05 20:09:59
回答 1查看 1.1K关注 0票数 0

我试图在TypeBuilder中使用C#动态生成一个带有函数的类,并让该函数调用另一个基本函数。

之所以需要这样做,是因为在Revit应用程序开发中,每个按钮都需要一个类来实现带有Execute函数的IExternalCommand。我希望动态创建按钮,并根据它们的ID在运行时处理它们的执行,因此我也需要动态创建类。

希望这段代码能够理解我正在寻找的内容(或者这里是http://pastebin.com/eehGKteT ):

代码语言:javascript
复制
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工作,我弄清楚了基本知识,但我似乎不知道如何使用Opcode来让类得到我想要的。

因此,我基本上是在寻求帮助编写generateClass(int )函数。任何帮助都将是非常感谢的!

编辑:

代码语言:javascript
复制
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:在程序集'DynamicAssemblyExample,Version=0.0.0.0,Culture=neutral,PublicKeyToken=null‘中键入'GeneratedClass99’的方法'Execute‘没有实现。

当我调用CreateType(..)时,会发生此错误在generateClass(.)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-06-06 14:27:52

首先,您必须修复所使用的参数类型。注意,message参数具有ref属性,因此您应该将typeof(String)更改为Type.GetType("System.String&")

在您必须声明您的execute方法实现(重写)从接口执行方法之后:

代码语言:javascript
复制
tb.DefineMethodOverride(mbExecute, typeof(IExternalCommand).GetMethod("Execute"));

我用一个控制台应用程序做了一些测试,通过上面的更改,我能够让它正常工作:

代码语言:javascript
复制
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 { }
    // =================================================================== //
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24069352

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档