编译和执行单行代码

Compiling and Executing single lines of code

我正在为项目创建/种类/自定义编译器。我正在做的是让用户在文本框中输入代码行,或者他们可以从文本文件中导入一些代码行。

过去几天我一直在尝试对此进行测试,但没有结果。我有一个名为 Pressure 的 class,其中我有一个名为 'emit' 的 public 方法,它只显示一个文本框,就像这样...

public void emit()
{
    MessageBox.Show("HOORA!");
}

我有一个名为 "testCompile.txt" 的文本文件,如下所示:

PressureTransducer pt = new PressureTransducer(0,0);
pt.emit();

当插入到 VS 中时,它应该编译得很好。之后,我尝试像这样编译文件...

String sourceName = @"C:\Users\Devic\Desktop\CompileTester\testCompile.txt";

CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");

CompilerParameters cp = new CompilerParameters();
cp.GenerateExecutable = true;
//cp.OutputAssembly = null;
cp.GenerateInMemory = true;
cp.TreatWarningsAsErrors = false;
CompilerResults cr = provider.CompileAssemblyFromFile(cp,
        sourceName);
if (cr.Errors.Count > 0)
{
    // Display compilation errors.
    Console.WriteLine("Errors building {0} into {1}",
        sourceName, cr.PathToAssembly);
    foreach (CompilerError ce in cr.Errors)
    {
        Console.WriteLine("  {0}", ce.ToString());
        Console.WriteLine();
    }
}
else
{
    // Display a successful compilation message.
    Console.WriteLine("Source {0} built into {1} successfully.",
        sourceName, cr.PathToAssembly);
}

但是 VS 给我错误:

c:\Users\Devic\Desktop\CompileTester\testCompile.txt(1,29) : error CS1518: Expected class, delegate, enum, interface, or struct

The thread 0x1290 has exited with code 0 (0x0).

对发生的事情有什么想法吗?

您需要将文本文件中的代码封装到可用的 class 和方法中。

下面是我使用了几年的代码,它允许 C# 脚本在我的应用程序中 运行,它甚至可以传递用户定义的变量。我在我的代码中传递了其他参数,让脚本编写者可以完全访问其他现有的 class 实例,但我将它们删除,因为它们是我的软件所独有的。如果您想提供对应用中任何现有 class 元素或表单的访问权限,您也可以这样做。

要使用您的 class PressureTransducer,您需要确保正确引用声明该类型的 DLL,并且命名空间包含在伪造代码封装的使用部分中。但是,我有一个内置部分可以自动引用您的 运行ning 程序当前引用的所有程序集,因此通常会自动处理所有事情。

此外,这将代码作为源代码的字符串并将程序集生成到内存中,因此没有磁盘访问 - 它 运行 非常快。

注意:其中使用了一个过时的函数,codeProvider.CreateCompiler();,但它对我仍然有效。不过我可能最终应该更新它。

private static object RunCSharpCode(string CSharpCode, bool ShowErrors, string StringParameter)
{
    try
    {
        #region Encapsulate Code into a single Method
        string Code =
            "using System;" + Environment.NewLine +
            "using System.Windows.Forms;" + Environment.NewLine +
            "using System.IO;" + Environment.NewLine +
            "using System.Text;" + Environment.NewLine +
            "using System.Collections;" + Environment.NewLine +
            "using System.Data.SqlClient;" + Environment.NewLine +
            "using System.Data;" + Environment.NewLine +
            "using System.Linq;" + Environment.NewLine +
            "using System.ComponentModel;" + Environment.NewLine +
            "using System.Diagnostics;" + Environment.NewLine +
            "using System.Drawing;" + Environment.NewLine +
            "using System.Runtime.Serialization;" + Environment.NewLine +
            "using System.Runtime.Serialization.Formatters.Binary;" + Environment.NewLine +
            "using System.Xml;" + Environment.NewLine +
            "using System.Reflection;" + Environment.NewLine +

            "public class UserClass" + Environment.NewLine +
            "{" + Environment.NewLine +
            "public object UserMethod( string StringParameter )" + Environment.NewLine +
            "{" + Environment.NewLine +
            "object Result = null;" + Environment.NewLine +
            Environment.NewLine +
            Environment.NewLine +

            CSharpCode +

            Environment.NewLine +
            Environment.NewLine +
            "return Result;" + Environment.NewLine +
            "}" + Environment.NewLine +
            "}";
        #endregion

        #region Compile the Dll to Memory

        #region Make Reference List
        Assembly[] FullAssemblyList = AppDomain.CurrentDomain.GetAssemblies();

        System.Collections.Specialized.StringCollection ReferencedAssemblies_sc = new System.Collections.Specialized.StringCollection();

        foreach (Assembly ThisAssebly in FullAssemblyList)
        {
            try
            {
                if (ThisAssebly is System.Reflection.Emit.AssemblyBuilder)
                {
                    // Skip dynamic assemblies
                    continue;
                }

                ReferencedAssemblies_sc.Add(ThisAssebly.Location);
            }
            catch (NotSupportedException)
            {
                // Skip other dynamic assemblies
                continue;
            }
        }

        string[] ReferencedAssemblies = new string[ReferencedAssemblies_sc.Count];
        ReferencedAssemblies_sc.CopyTo(ReferencedAssemblies, 0);
        #endregion

        Microsoft.CSharp.CSharpCodeProvider codeProvider = new Microsoft.CSharp.CSharpCodeProvider();
        System.CodeDom.Compiler.ICodeCompiler CSharpCompiler = codeProvider.CreateCompiler();
        System.CodeDom.Compiler.CompilerParameters parameters = new System.CodeDom.Compiler.CompilerParameters(ReferencedAssemblies);
        parameters.GenerateExecutable = false;
        parameters.GenerateInMemory = true;
        parameters.IncludeDebugInformation = false;
        parameters.OutputAssembly = "ScreenFunction";

        System.CodeDom.Compiler.CompilerResults CompileResult = CSharpCompiler.CompileAssemblyFromSource(parameters, Code);
        #endregion

        if (CompileResult.Errors.HasErrors == false)
        { // Successful Compile
            #region Run "UserMethod" from "UserClass"
            System.Type UserClass = CompileResult.CompiledAssembly.GetType("UserClass");
            object Instance = Activator.CreateInstance(UserClass, false);
            return UserClass.GetMethod("UserMethod").Invoke(Instance, new object[] { StringParameter });
            #endregion
        }
        else // Failed Compile
        {
            if (ShowErrors)
            {
                #region Show Errors
                StringBuilder ErrorText = new StringBuilder();

                foreach (System.CodeDom.Compiler.CompilerError Error in CompileResult.Errors)
                {
                    ErrorText.Append("Line " + (Error.Line - 1) +
                        " (" + Error.ErrorText + ")" +
                        Environment.NewLine);
                }

                MessageBox.Show(ErrorText.ToString());
                #endregion

            }
        }
    }
    catch (Exception E)
    {
        if (ShowErrors)
            MessageBox.Show(E.ToString());
    }

    return null;
}

您可以考虑查看新的 Roslyn 编译器。您将字符串传递给脚本引擎 class 中的 Execute 方法,它将即时执行代码。

public class CSharpScriptEngine
{
    private static Script _previousInput;
    private static Lazy<object> _nextInputState = new Lazy<object>();
    public static object Execute(string code)
    {
        var script = CSharpScript.Create(code, ScriptOptions.Default).WithPrevious(_previousInput);
        var endState = script.Run(_nextInputState.Value);
        _previousInput = endState.Script;
        _nextInputState = new Lazy<object>(() => endState);
        return endState.ReturnValue;
    }
}

请参阅 this article 了解信用和完整实施。