如何动态创建从接口派生的类,然后保留它以供以后使用

时间:2014-06-02 22:55:21

标签: c# class interface system.reflection

我正在编写一个可以自学的程序。

示例:

  1. 一句话' day'是打字
  2. 查找名为' day'
  3. 的界面
  4. 它存在吗?
  5. 否=>基于该接口创建类(并实现接口)并将其保存以供下次使用,然后创建该类的实例
  6. 是=>创建该类的实例
  7. Google给了我这个:How to dynamically create a class in C#?

    这个解决方案是我能想到的最接近我的方案的解决方案,但它假设您已经知道需要多少属性,并且根本不会实现接口。

    我对system.reflection没有任何经验,但我渴望学习!

    任何人都知道我的案例?

    任何帮助都是适当的。

    谢谢!

    编辑:我的解决方案 (因为没有人给我一个直接的答案,我自己想出来了)

        public void createObject(string name)
        {
            //Namespace where the interfaces are located
            string strnamespace = "Intelligence.Omnia.Categories";
            //Get interfacecollection
            List<Type> interfaceCollection = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsInterface && t.Namespace == strnamespace).ToList();
            foreach (Type myinterface in interfaceCollection)
            {
                //interface names
                List<string> interfaceNames = new List<string>();
    
                if (myinterface.Name == name)
                {
                    //Add interface name
                    interfaceNames.Add(myinterface.Name);
    
                    //Add current interfaceproperties
                    List<PropertyInfo> myProps = myinterface.GetProperties().ToList();
    
                    //Does the current interface inhiretes from other interfaces?
                    foreach (Type inhiretences in myinterface.GetInterfaces())
                    {
                        //Add interface name
                        interfaceNames.Add(inhiretences.Name);
    
                        //Add those properties aswell!
                        foreach (PropertyInfo pi in inhiretences.GetProperties())
                        {
                            myProps.Add(pi);
                        }
                    }
    
                    createType(name, myProps, interfaceNames);
                }
            }
        }
        static void createType(string name, List<PropertyInfo> props, List<string> interfacesnames)
        {
            //create instance of CSharpCodeProvider
            CSharpCodeProvider csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });
    
            //DLL
            string pathDLL = AppDomain.CurrentDomain.BaseDirectory + "Objects.dll";
            CompilerParameters parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.dll", "System.Linq.dll", "System.Threading.Tasks.dll", "Intelligence.dll" });
            parameters.OutputAssembly = pathDLL;
            parameters.GenerateExecutable = false;
            ICodeCompiler icc = csc.CreateCompiler();
    
            //>>>>Generated CODE
    
            //Add namespaces
            CodeCompileUnit compileUnit = new CodeCompileUnit();
            CodeNamespace ns = new CodeNamespace("Objects");
            compileUnit.Namespaces.Add(ns);
            ns.Imports.Add(new CodeNamespaceImport("System"));
            ns.Imports.Add(new CodeNamespaceImport("System.Collections.Generic"));
            ns.Imports.Add(new CodeNamespaceImport("System.Linq"));
            ns.Imports.Add(new CodeNamespaceImport("System.Text"));
            ns.Imports.Add(new CodeNamespaceImport("System.Threading.Tasks"));
            ns.Imports.Add(new CodeNamespaceImport("Intelligence"));
            ns.Imports.Add(new CodeNamespaceImport("Intelligence.Omnia"));
            ns.Imports.Add(new CodeNamespaceImport("Intelligence.Omnia.Categories"));
    
            //Define your class
            CodeTypeDeclaration classType = new CodeTypeDeclaration("Object"+name);
            classType.Attributes = MemberAttributes.Public; //make it public
            foreach(string interfaceName in interfacesnames) //let it inherit from the interfaces
            {
                classType.BaseTypes.Add(interfaceName);
            }
            ns.Types.Add(classType);
    
            //Add constructor
            CodeConstructor constr = new CodeConstructor();
            constr.Attributes = MemberAttributes.Public;
            classType.Members.Add(constr);
    
    
            //Add all the properties
            foreach (var prop in props)
            {
                //If you want private fields
                //CodeMemberField field = new CodeMemberField(prop.PropertyType, prop.Name);
                //classType.Members.Add(field);
    
                CodeMemberProperty property = new CodeMemberProperty();
                property.Attributes = MemberAttributes.Public | MemberAttributes.Final;
                property.Type = new CodeTypeReference(prop.PropertyType);
                property.Name = prop.Name;
                property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), prop.Name)));
                property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), prop.Name), new CodePropertySetValueReferenceExpression()));
                classType.Members.Add(property);
            }
    
            //Write the file for later use
            TextWriter tw = new StreamWriter(new FileStream(AppDomain.CurrentDomain.BaseDirectory + "Objects\\" + name + ".cs", FileMode.Create));
            csc.GenerateCodeFromCompileUnit(compileUnit, tw, null);
            tw.Close();
    
            //Compile the class
            CompilerResults results = icc.CompileAssemblyFromFile(parameters, AppDomain.CurrentDomain.BaseDirectory + "Objects\\" + name + ".cs");
            results.Errors.Cast<CompilerError>().ToList().ForEach(error => System.Windows.Forms.MessageBox.Show(error.ErrorText));
    
        }
    

1 个答案:

答案 0 :(得分:3)

正如@Stefan所说,您可以使用内置于.net 4.0+中的DLR动态创建类型,但您也可以在CodeDom中使用旧的基于反射的机制。我的建议是看看IronPython,因为它可以很容易地做你想要的。但是,如果您需要C#,那么您需要了解C#已编译,您需要在System.CodeDom.Compiler中使用编译。

没有什么可以从单词Day推断知识到界面日是什么 - 你必须提供这种方式。但是,如果您知道接口将存在的规则,则可以动态创建它。您还可以进一步根据界面创建其他类型。再一次,没有代码可以神奇地编写 - 你需要提供代码语义和语法。

但是如果你有这个,你可以将代码分成多个程序集(CompileAssemblyFromSource)。然后动态加载程序集以加载类型(步骤2)。您可以在运行时创建类型和程序集(请参阅操作系统:Generating DLL assembly dynamically at run time)。

此代码来自上面链接的SO答案,并向您展示如何从某些代码串中进行汇编。

using System.CodeDom.Compiler;
using System.Diagnostics;
using Microsoft.CSharp;

CSharpCodeProvider codeProvider = new CSharpCodeProvider();
ICodeCompiler icc = codeProvider.CreateCompiler();
System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = false;
parameters.OutputAssembly = "My_Assembly_Day.dll";
CompilerResults results = icc.CompileAssemblyFromSource(parameters, ".... some C# code ....");

此SO答案向您展示如何加载程序集以发现类型:

https://stackoverflow.com/a/14184863/30225

编辑:回答评论。

  1. 你可以随意命名你的程序集 - 我的建议是用接口/类组合来命名它。以上只是一个如何命名的例子
  2. 您可以在加载时加载现有(预先创建的程序集)(程序的第一步),也可以在需要时在运行时动态加载程序集。
  3. 一旦加载了程序集,就可以使用类型(Activator.CreateInstance)。
  4. 如果您进入步骤并且例如类型IDay不可用,您可以使用类型文本动态创建它。当然,要使CodeDom编译器工作,您需要确保将此代码引用的所有内容提供给编译单元。并将其编译到磁盘上的程序集中。您可以创建界面&amp;同一时间或两步的课程。
  5. 完成第4步后,您可以像步骤3和步骤3中那样加载该程序集。 4。
  6. 在一天结束时答案归结为:

    1. 从现有类型集
    2. 中查找现有类型
    3. 如果现有类型不可用,请在动态组件中创建它。
    4. 加载该类型
    5. 实例化该类型。
    6. 使用该类型时会出现问题。您可以使用CodeDom创建更多代码(实际上,这使您能够重新创建可能已经存在的类BTW),或者您可以在代码中动态使用类型。第一个具有编译开销,而第二个具有编写没有硬编码类型的代码的复杂性 - 使用C#动态关键字完成的工作非常简单。无论哪种方式,当动态使用类型时,这是一种非常简单的mill .net编码技术,许多现有应用程序使用这样的技术来管理插件。

      警告:

      请记住.net中最小的可卸载单元是AppDomain。加载程序集时,将它们加载到AppDomain中。如果要卸载程序集(以及类型)来替换它们实例,则需要取消对AppDomain的取消。这意味着您需要确保将任何动态加载的程序集加载到新的AppDomain中,然后可以在需要时卸载它。