我想动态生成程序集,它可以具有不同结构的函数。为了更准确,这些函数可以是递归的,它们可以在同一个程序集中调用其他函数等。我找到了System.Reflection
模块,它在理论上提供了执行此操作的工具,但实际上我遇到了这种方法的许多缺点。例如 - 我无法通过TypeBuilder
和MethodBuilder
类生成递归函数,因为将抛出异常(使用不完整类型)。我了解到我可以通过IlGenerator
生成自递归函数 - 但它太麻烦了 - 我希望有一种更简单的方法可以做到这一点。
这是我的程序,它演示了这个问题(在生成方法Fact
时抛出以下异常:
Exception thrown: 'System.NotSupportedException' in mscorlib.dll Additional information: Specified method is not supported..
代码:
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Linq.Expressions;
namespace GenericFuncs
{
public class ProgramBuilder
{
public Type createMyProgram()
{
var assmName = new AssemblyName("DynamicAssemblyExample");
var assmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assmName, AssemblyBuilderAccess.RunAndSave);
var moduleBuilder = assmBuilder.DefineDynamicModule(assmName.Name, assmName.Name + ".dll");
var myProgramType = buildMyProgram(moduleBuilder, moduleBuilder.DefineType("MyProgram", TypeAttributes.Public));
assmBuilder.Save(assmName.Name + ".dll");
return myProgramType;
}
private Type buildMyProgram(ModuleBuilder mb, TypeBuilder programBuilder)
{
buildFactFunction2(mb, mb.GetType("MyProgram"), programBuilder.DefineMethod("InfLoop", MethodAttributes.Public | MethodAttributes.Static));
buildFactFunction(mb, mb.GetType("MyProgram"), programBuilder.DefineMethod("Fact", MethodAttributes.Public | MethodAttributes.Static));
return programBuilder.CreateType();
}
private void buildFactFunction(ModuleBuilder mb, Type program_type, MethodBuilder methodBuilder)
{
var param = Expression.Parameter(typeof(int), "n");
var lambda = Expression.Lambda(Expression.Call(methodBuilder, param), param);
lambda.CompileToMethod(methodBuilder);
}
static public void my_print(string s)
{
Console.WriteLine(s);
}
private void buildFactFunction2(ModuleBuilder mb, Type program_type, MethodBuilder methodBuilder)
{
var il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Ldstr, "a");
il.Emit(OpCodes.Call, typeof(ProgramBuilder).GetMethod("my_print"));
il.Emit(OpCodes.Call, methodBuilder);
il.Emit(OpCodes.Ret);
}
}
class Program
{
static void Main(string[] args)
{
var pbuilder = new ProgramBuilder();
var ptype = pbuilder.createMyProgram();
Console.ReadLine();
}
}
}
感谢任何帮助。
答案 0 :(得分:4)
Expressions C#中的反映有其局限性(虽然对于简单的用例仍然非常强大)。据我所知,如果您需要所描述的功能(甚至是发射组件的基本要求),Emit是可行的方法。
几年前,我在一些最小的动态代码生成用例中非常有效地使用了RunSharp。它消除了大部分IL痛苦。例如,在this code中,我在运行时创建了一个代理包装器。
您还可以查看Castle Project用于代码生成的内容,例如,DynamicProxy非常受欢迎。
答案 1 :(得分:3)
这可能不是你想要的,但你有没有想过 CodeDom ?
缺点是它不会真正动态,因为你必须加载程序集
Dynamic Source Code Generation and Compilation
正如 Steven Jeuris所指出 CodeDom 和 Emit Reflection.Emit vs CodeDOM
CodeDom生成C#源代码,通常在生成代码时使用,作为解决方案的一部分并在IDE中编译(用于 例如,LINQ to SQL类,WSDL,XSD都以这种方式工作)。在这 您还可以使用部分类来自定义生成的场景 码。效率较低,因为它会生成C#源 运行编译器来解析它(再次!)并编译它。您可以 使用相对高级的构造生成代码(类似于C# 表达式语句,例如循环。
Reflection.Emit会生成一个IL,因此它会直接生成一个程序集,该程序集也只能存储在内存中。结果是更多 您必须生成低级别的IL代码(值存储在 堆;循环必须使用跳转来实现,因此生成任何 更复杂的逻辑有点困难。
来自MSDN的简单CodeDom Exmaple
public Sample()
{
targetUnit = new CodeCompileUnit();
CodeNamespace samples = new CodeNamespace("CodeDOMSample");
samples.Imports.Add(new CodeNamespaceImport("System"));
targetClass = new CodeTypeDeclaration("CodeDOMCreatedClass");
targetClass.IsClass = true;
targetClass.TypeAttributes =
TypeAttributes.Public | TypeAttributes.Sealed;
samples.Types.Add(targetClass);
targetUnit.Namespaces.Add(samples);
}
public void AddFields()
{
// Declare the widthValue field.
CodeMemberField widthValueField = new CodeMemberField();
widthValueField.Attributes = MemberAttributes.Private;
widthValueField.Name = "widthValue";
widthValueField.Type = new CodeTypeReference(typeof(System.Double));
widthValueField.Comments.Add(new CodeCommentStatement(
"The width of the object."));
targetClass.Members.Add(widthValueField);
// Declare the heightValue field
CodeMemberField heightValueField = new CodeMemberField();
heightValueField.Attributes = MemberAttributes.Private;
heightValueField.Name = "heightValue";
heightValueField.Type =
new CodeTypeReference(typeof(System.Double));
heightValueField.Comments.Add(new CodeCommentStatement(
"The height of the object."));
targetClass.Members.Add(heightValueField);
}
public void GenerateCSharpCode(string fileName)
{
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CodeGeneratorOptions options = new CodeGeneratorOptions();
options.BracingStyle = "C";
using (StreamWriter sourceWriter = new StreamWriter(fileName))
{
provider.GenerateCodeFromCompileUnit(
targetUnit, sourceWriter, options);
}
}
static void Main()
{
Sample sample = new Sample();
sample.AddFields();
//sample.AddProperties();
//sample.AddMethod();
//sample.AddConstructor();
//sample.AddEntryPoint();
sample.GenerateCSharpCode(outputFileName);
}
您可以使用多种方法之一来运行代码
private static Assembly CompileSourceCodeDom(string sourceCode)
{
CodeDomProvider cpd = new CSharpCodeProvider();
var cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("System.dll");
cp.GenerateExecutable = false;
CompilerResults cr = cpd.CompileAssemblyFromSource(cp, sourceCode);
return cr.CompiledAssembly;
}
private static void ExecuteFromAssembly(Assembly assembly)
{
Type fooType = assembly.GetType("Foo");
MethodInfo printMethod = fooType.GetMethod("Print");
object foo = assembly.CreateInstance("Foo");
printMethod.Invoke(foo, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture);
}