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

在当前表单(代码)的上下文中,从字符串动态运行C#代码,c#,.net,winforms,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."); }

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

例如,在
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
,但这似乎只能将代码作为单独的程序进行编译

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


我不担心程序的安全性。

基本上,您想要做的是动态编译并向当前程序添加代码。这不是不可能的,有多种方法可以做到这一点

这类功能最常见的用途是插件和脚本系统。然而,这两个方面都有一些警告

最大的缺点之一是,要运行已编译的代码(不管您是如何编译的),您需要使用标准的load方法将其作为程序集加载到应用程序域中。加载库后,如果不卸载应用程序域,实际上无法卸载它,因此在某些情况下会产生问题

<>如果这是你正在编写的脚本,我会认真考虑使用预先构建的脚本库(还有很多)。有些人使用巧妙的技巧使这项工作很好,并为你们做了很多艰苦的工作。。。例如

但是要做好准备!插件和脚本系统一开始很简单,但它们在稳定和可操作性方面却非常灵活。我建议首先探索你的问题领域,并确保你没有试图重新发明轮子。。。有许多选项可以在运行时安全地序列化和加载对象参数,而无需担心


祝你好运

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

  • 您应该将动态代码创建为dll中的类
  • 你的类应该实现一个特定的接口或者有一个已知的方法,比如
    Run
    。因此,您可以稍后在代码编译时调用该方法
  • 您的类或已知方法应该接受一些参数来接收上下文变量。这些上下文变量可以包括
    表单
    作为参数。您还可以将上下文参数封装在类/接口中,或者为了保持简单,您可以依靠
    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);
    }
}
公共字符串SomePublicField=“Hello!”; 私有无效按钮1\u单击(对象发送者,事件参数e){ var csc=新的CSharpCodeProvider(); var参数=新编译器参数(新[]{ “mscorlib.dll”, “System.Windows.Forms.dll”, “System.dll”, “System.Drawing.dll”, “System.Core.dll”, “Microsoft.CSharp.dll”}); var results=csc.compileasemblyfromsource(参数, @" 使用System.Windows.Forms; 使用系统图; 公共类样本 { 公共无效剂量测定(动态形式) { var b=新按钮(); b、 Text=form.Text; b、 单击+=(s,e)=>{MessageBox.Show(form.SomePublicField);}; 表.控件.添加(b); } }"); //检查编译是否成功,运行代码 如果(!results.Errors.HasErrors){ var t=results.CompiledAssembly.GetType(“示例”); 动态o=Activator.CreateInstance(t); o、 DoSomething(这个); } 否则{ var errors=string.Join(Environment.NewLine, results.Errors.Cast().Select(x=>x.ErrorText)); MessageBox.Show(错误); } }
这很困难,因为允许抽象代码在启动它的应用程序所在的安全上下文中运行是危险的。如果你真的想这样做,你必须将它编译成一个程序集,加载到应用程序域,然后调用该代码。你不能向当前程序添加代码,因为它的映像已经加载。但是你可以编译一个单独的DLL并将表单实例传递给它的一个函数,然后它可以在其中添加控件。也许您可以解释为什么在应用程序中需要C#代码而不是提供控件类型、参数和坐标的元组。@RezaAghaei我需要以用户可以修改的形式运行代码(通过文本文件),如果用户选择这样做的话。我需要在当前表单的上下文中使用它,因为我希望它们能够在这样的代码中使用某些变量和函数,因此它们可以向表单添加控件。我并不担心程序的安全性。只是需要注意,如果字符串包含c#6或更高版本(或者是5+?),那么这将不起作用。您需要添加适当的nugetpackages@pinkfloydx33该示例是在.NET 4.7.1中使用标准Windows窗体项目模板编写的,该模板不依赖于包。我的意思是,框架中包含的
CSharpCodeProvider
无法编译c#6代码。如果代码(在传递给
compileasemblyfromsource
的字符串中)包含c#6构造,则如果没有额外的nuget包,这将失败。在这种情况下没有,因为没有这样的代码——我只是简单地为OP或任何其他偶然发现这个答案(我投了赞成票)的人调用它,如果他们修改它或将它应用于其他场景。[这是我的理解,如果我错了,我会很高兴被纠正]感谢@pinkfloydx33原始po的反馈