65.9K
CodeProject 正在变化。 阅读更多。
Home

运行时编译和执行代码

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.48/5 (24投票s)

2004年12月11日

3分钟阅读

viewsIcon

121373

downloadIcon

3216

本文描述了在运行时运行未编译代码是多么容易。

引言

嗯,这是我在 The Code Project 上的第一篇文章,所以我会尽力保持主题。 我最近对创建一个优先级计算器库很感兴趣。 当我完成了大约 10% 的时候,我突然有了一个想法。 如果我可以直接使用 C# 代码,编译并执行它,然后获得数学方程式的结果,会怎么样。

嗯,我想这不会很容易,但一定有办法让它奏效。 事实证明我错了,它确实很容易。 但是,我想让它更容易。 我想将这个概念抽象到足够程度,我所需要做的就是传入代码(或者对于更复杂的情况,代码和一些描述要运行什么的参数)。

private Assembly BuildAssembly(string code)
{
    Microsoft.CSharp.CSharpCodeProvider provider = 
       new CSharpCodeProvider();
    ICodeCompiler compiler = provider.CreateCompiler();
    CompilerParameters compilerparams = new CompilerParameters();
    compilerparams.GenerateExecutable = false;
    compilerparams.GenerateInMemory = true;
    CompilerResults results = 
       compiler.CompileAssemblyFromSource(compilerparams, code);
    if (results.Errors.HasErrors)
    {
        StringBuilder errors = new StringBuilder("Compiler Errors :\r\n");
        foreach (CompilerError error in results.Errors )
        {
            errors.AppendFormat("Line {0},{1}\t: {2}\n", 
                   error.Line, error.Column, error.ErrorText);
        }
        throw new Exception(errors.ToString());
    }
    else
    {
        return results.CompiledAssembly;
    }
}

这是我的库的核心。 此函数构建 Assembly 对象并将其存储在内存中。 如您所见,我使用的是 C# 代码提供程序。 编译 VB.NET 代码(或者任何其他代码)就像更改提供程序类一样简单。 我告诉编译器通过将 GenerateExecutable 设置为 false 来生成一个 DLL,然后告诉它将 DLL 保存在内存中而不是在本地磁盘上创建文件。 任何错误都将被捕获并显示,就像它们在 Visual Studio .NET 中一样。 最后,如果编译成功,则返回 Assembly 对象。

public object ExecuteCode(string code, 
    string namespacename, string classname,
    string functionname, bool isstatic, params object[] args)
{
    object returnval = null;
    Assembly asm = BuildAssembly(code);
    object instance = null;
    Type type = null;
    if (isstatic)
    {
        type = asm.GetType(namespacename + "." + classname);
    }
    else
    {
        instance = asm.CreateInstance(namespacename + "." + classname);
        type = instance.GetType();
    }
    MethodInfo method = type.GetMethod(functionname);
    returnval = method.Invoke(instance, args);
    return returnval;
}

这是我的库的公共接口。 非常简单,传入代码(甚至可以从磁盘上的 C# 文件中读取),传入包含执行类的命名空间,传入执行函数的类,传入要执行的函数,指定执行函数是否为静态,最后是 params 数组,用于执行函数调用的任何参数。 通过这种设置,您理论上可以执行使用多个命名空间的代码。 只要代码良好并且您指定了正确的参数,就可以完成。

目前这个库无法完成的一件事是运行调用外部 DLL 的引用命名空间的代码。 这是可能的,它是编译器参数的一部分,我只是还没有设置它。

基本上就是这样,这就是您在运行时编译和执行未编译代码所需的所有代码。 相当巧妙的东西,它的实际应用非常广泛。 就我个人而言,我现在将继续我的计算器项目,但请随意使用此库或做任何事情。

该库还包括一个用于进行数学计算的模板设置。

我还包含了一个带有该库的小型测试应用程序。 这没什么特别的,只是我用来调试这个库的东西。 指示非常简单,唯一令人困惑的命令是 Execute 命令 '/x' -- 它接受多个参数并使用以下语法

/x myNamespace MyClass MyFunction False param1,param2,param3,etc

False 表明 MyFunction 不是静态的。

在运行时编译和执行代码 - CodeProject - 代码之家
© . All rights reserved.