使用DLR运行用CompileAssemblyFromSource生成的代码?

时间:2012-06-06 12:45:09

标签: c# .net codedom dynamic-language-runtime

关注这个excellent answer,我想知道使用dynamic关键字的DLR是否允许更简洁的方式为生成的程序集编写代码。

例如,可以上述答案的代码:

using (Microsoft.CSharp.CSharpCodeProvider foo = 
           new Microsoft.CSharp.CSharpCodeProvider())
{
    var res = foo.CompileAssemblyFromSource(
        new System.CodeDom.Compiler.CompilerParameters() {  
            GenerateInMemory = true 
        }, 
        "public class FooClass { public string Execute() { return \"output!\";}}"
    );

    var type = res.CompiledAssembly.GetType("FooClass");
    var obj = Activator.CreateInstance(type);
    var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
}

变得像:

using (Microsoft.CSharp.CSharpCodeProvider foo = 
           new Microsoft.CSharp.CSharpCodeProvider())
{
    var res = foo.CompileAssemblyFromSource(
        new System.CodeDom.Compiler.CompilerParameters() {  
            GenerateInMemory = true 
        }, 
        "public class FooClass { public string Execute() { return \"output!\";}}"
    );

    var type = res.CompiledAssembly.GetType("FooClass");
    dynamic obj = Activator.CreateDynamicInstance(type);
    var output = obj.Execute();
}

2 个答案:

答案 0 :(得分:7)

是的,你可以做到这一点并且效果很好。但是,虽然使用动态关键字更方便,但它使用后期绑定,并且在这个意义上仍然是不安全的,因为显式使用反射。如果您的设计允许,最好使用共享接口或基类进行早期绑定。您可以通过在程序集或第三个共享程序集中创建公共类型,然后从正在动态编译的新程序集中添加对该程序集的引用来完成此操作。然后,在生成的代码中,您可以从引用的程序集中的该共享类型继承。例如,创建一个接口:

public interface IFoo
{
    string Execute();
}

然后像这样动态编译程序集:

using (Microsoft.CSharp.CSharpCodeProvider foo = new Microsoft.CSharp.CSharpCodeProvider())
{
    var params = new System.CodeDom.Compiler.CompilerParameters();
    params.GenerateInMemory = true;

    // Add the reference to the current assembly which defines IFoo
    params.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);

    // Implement the IFoo interface in the dynamic code
    var res = foo.CompileAssemblyFromSource(params, "public class FooClass : IFoo { public string Execute() { return \"output!\";}}");
    var type = res.CompiledAssembly.GetType("FooClass");

    // Cast the created object to IFoo
    IFoo obj = (IFoo)Activator.CreateInstance(type);

    // Use the object through the IFoo interface
    obj.Execute();
}

根据您对动态代码的控制程度,这可能会也可能不会,但是当它成功时,进行编译时类型检查会很好。例如,如果您尝试执行:

IFoo obj = (IFoo)Activator.CreateInstance(type);
obj.Execcute();

第二行会立即无法编译,因为拼写错误,而使用动态关键字或反射,该行会成功编译但会导致运行时异常。例如,以下内容不会出现编译时错误:

dynamic obj = Activator.CreateDynamicInstance(type);
obj.Execcute();

答案 1 :(得分:0)

这是DLR设计的场景之一。您可以通过这种方式调用动态加载类型的成员,同时通过手动调用.GetMethod().Invoke()来避免所有额外的输入。