我正在编写一个可以自学的程序。
示例:
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));
}
答案 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
编辑:回答评论。
在一天结束时答案归结为:
使用该类型时会出现问题。您可以使用CodeDom创建更多代码(实际上,这使您能够重新创建可能已经存在的类BTW),或者您可以在代码中动态使用类型。第一个具有编译开销,而第二个具有编写没有硬编码类型的代码的复杂性 - 使用C#动态关键字完成的工作非常简单。无论哪种方式,当动态使用类型时,这是一种非常简单的mill .net编码技术,许多现有应用程序使用这样的技术来管理插件。
警告:
请记住.net中最小的可卸载单元是AppDomain。加载程序集时,将它们加载到AppDomain中。如果要卸载程序集(以及类型)来替换它们实例,则需要取消对AppDomain的取消。这意味着您需要确保将任何动态加载的程序集加载到新的AppDomain中,然后可以在需要时卸载它。