在当前表单(代码)的上下文中从字符串动态运行C#代码

时间:2018-06-18 02:58:31

标签: c# .net winforms

我需要在当前表单(代码)的上下文中运行来自文本文件的代码。其中一个要求是让代码创建并向当前表单添加新控件。

例如,在Form1.cs

using System.Windows.Forms;
...

public int[] someCoords = { 20, 10 };
public string someImportantString = "Hello";

public void SayHello() {
    MessageBox.Show("Hello world.");
}

private void runCodeInForm() {
    // theCode will be read from a text file
    string theCode = @"
        // Has System.Windows.Forms already added in form
        Button newButton = new Button();
        newButton.Text = someImportantString; // Hello
        newButton.Location = new Point(someCoords[0], someCoords[1]); // 20, 10

        // Add this button to the current form
        this.Controls.Add(newButton);

        this.SayHello(); // Says hello. Just an example function.
    ";

    // Execute theCode in the current form
    CodeRunner.Execute(theCode, this);
}

我尝试使用CSharpCodeProvider,但似乎只能将代码编译为单独的程序。

我希望这样,因为我希望用户能够将此代码(文本文件)更改为他们想要的内容。它不仅仅是用于创建控件,而是需要该功能。

我并不担心该计划的安全性。

2 个答案:

答案 0 :(得分:3)

考虑以下几点来解决问题:

  • 您应该将动态代码创建为dll中的类。
  • 您的类应该实现特定的接口或具有Run等已知方法。因此,您可以在代码编译时稍后调用该方法。
  • 您的类或已知方法应接受一些参数来接收上下文变量。这些上下文变量可以包含Form作为参数。您还可以将上下文参数封装在类/接口中,或者保持简单,您可以依赖dynamic来传递参数。

然后运行动态代码,首先编译它,然后将上下文参数传递给类或已知方法,并调用已知方法。

示例

这是一个快速而又脏的示例,说明如何在运行时编译和运行代码并让代码使用您的上下文:

public string SomePublicField = "Hello!";
private void button1_Click(object sender, EventArgs e) {
    var csc = new CSharpCodeProvider();
    var parameters = new CompilerParameters(new[] {
        "mscorlib.dll",
        "System.Windows.Forms.dll",
        "System.dll",
        "System.Drawing.dll",
        "System.Core.dll",
        "Microsoft.CSharp.dll"});
    var results = csc.CompileAssemblyFromSource(parameters,
    @"
    using System.Windows.Forms;
    using System.Drawing;
    public class Sample 
    {
        public void DoSomething (dynamic form) 
        {
            var b = new Button();
            b.Text = form.Text;
            b.Click += (s,e)=>{MessageBox.Show(form.SomePublicField);};
            form.Controls.Add(b);
        }
    }");
    //Check if compilation is successful, run the code
    if (!results.Errors.HasErrors) {
        var t = results.CompiledAssembly.GetType("Sample");
        dynamic o = Activator.CreateInstance(t);
        o.DoSomething(this);
    }
    else {
        var errors = string.Join(Environment.NewLine,
            results.Errors.Cast<CompilerError>().Select(x => x.ErrorText));
        MessageBox.Show(errors);
    }
}

答案 1 :(得分:1)

基本上你想要做的是动态编译并将代码添加到当前程序中。这并非不可能,并且有各种方法可以做到这一点。

此类功能的最常见用途是插件和脚本系统。但是,对这两者都有一些警告。

最大的缺点之一是运行已编译的代码(但是你正在这样做),你需要使用标准的Load方法将它作为程序集加载到app域中。加载库后,实际上无法在卸载应用程序域时卸载它,因此在某些情况下会产生问题。

如果这是你正在编写的脚本,我会认真考虑使用预先构建的脚本库(并且有很多)。有些人使用漂亮的技巧来使这项工作做得很好,并为你做了很多艰苦的工作......例如http://csscript.net/

然而做好准备!插件和脚本系统起步很容易,但它们非常繁琐,以使稳定和可行。我建议首先探索你的问题的领域,并确保你没有尝试重新发明轮子...有许多选项可以在运行时安全地序列化和加载对象参数而不用大惊小怪

祝你好运