Use DLR to run code generated with CompileAssemblyFromSource?

假装没事ソ 提交于 2019-12-01 18:11:20

问题


Following up on this excellent answer, I'm wondering if the DLR using the dynamic keyword can allow a less verbose way of writing code for the generated assembly.

For example, can the aforementioned answer's code:

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[] { });
}

become something like:

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();
}

回答1:


Yes, you can do that and it works well. However, while using the dynamic keyword is more convenient, it utilizes late-binding and is still just as unsafe, in that sense, as explicitly using reflection. If your design allows it, it is even better to use a shared interface or base class for early-binding. You can do this by creating a public type in your assembly or in a third, shared assembly, and then add a reference to that assembly from the new one you are dynamically compiling. Then, in the generated code, you can inherit from that shared type in the referenced assembly. For instance, create an interface:

public interface IFoo
{
    string Execute();
}

Then dynamically compile the assembly like this:

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();
}

Depending on how much control you have over the dynamic code, this may or may not be possible, but when it is, it's nice to have the compile-time type-checking. For instance, if you tried to execute:

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

That second line would immediately fail to compile because it's spelled wrong, whereas with the dynamic keyword or reflection, that line would successfully compile but it would cause a run-time exception. For instance, the following will not get a compile-time error:

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



回答2:


That is one of the scenarios that the DLR was designed for. You can use it that way to invoke members of a dynamically loaded type while avoiding all of the extra typing from manually calling .GetMethod() and .Invoke().



来源:https://stackoverflow.com/questions/10914484/use-dlr-to-run-code-generated-with-compileassemblyfromsource

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!