是否可以执行表示为字符串的C#代码?

时间:2014-01-29 23:45:55

标签: c# string interpreter

在我的表格上,我点击了一下按钮

private void button1_Click(object sender, EventArgs e)
{
    do something                           
}

如何在点击时加载我从文本文件中执行的操作,例如我的文本文件如下所示:

MessageBox.Show("hello");
label1.Text = "Hello";

单击它会在我的文本文件中执行所有操作(如果可能)。

3 个答案:

答案 0 :(得分:4)

这是一个非常简单的例子,只是为了证明这是可能的。基本上,您使用CodeDomProvider在运行时编译源代码,然后使用反射执行。

var provider = CodeDomProvider.CreateProvider("C#");
string src=@"
    namespace x
    {
        using System;
        public class y
        {
            public void z()
            {
                Console.WriteLine(""hello world"");
            }
        }
    }
";
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
    var type = result.CompiledAssembly.GetType("x.y");
    var instance = Activator.CreateInstance(type);
    type.GetMethod("z").Invoke(instance, null);
}

修改

正如@Agat指出的那样,OP似乎需要一种脚本框架(它使用label1,这是当前对象的一个​​属性),而我上面的回答显然没有提供。我能想到的最好的是一个有限的解决方案,即要求将依赖关系明确指定为“脚本”中的参数。例如,编写如下脚本代码:

string src = @"
namespace x
{
    using System.Windows;
    public class y
    {
        public void z(Label label1)
        {
            MessageBox.Show(""hello"");
            label1.Text = ""Hello"";
        }
    }
}
";

现在您可以让调用者检查参数,并使用反射再次从当前上下文传递它们:

var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
    var type = result.CompiledAssembly.GetType("x.y");
    var instance = Activator.CreateInstance(type);
    var method = type.GetMethod("z");
    var args = new List<object>();

    // assume any parameters are properties/fields of the current object
    foreach (var p in method.GetParameters())
    {
        var prop = this.GetType().GetProperty(p.Name);
        var field = this.GetType().GetField(p.Name);
        if (prop != null)
            args.Add(prop.GetValue(this, null));
        else if (field != null);
            args.Add(field.GetValue(this));
        else
            throw new InvalidOperationException("Parameter " + p.Name + " is not found");
    }
    method.Invoke(instance, args.ToArray());
}

答案 1 :(得分:1)

与其他答案一样,实施起来并不容易,可以通过反思来完成,具体取决于脚本的高级程度。

没有人 @BrankoDimitrijevic提到 Roslyn ,这是一个很棒的工具。 http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx

它在相当长的一段时间内没有更新(2012年9月)并且没有实现C#的所有功能,但是,当我玩这个版本时它确实有很多实现。

通过添加程序集作为脚本会话的参考,您可以访问所有程序集的类型和脚本。它还支持返回值,因此您可以返回脚本方法生成的任何数据。

You can find what isn't implemented here.

以下是我刚刚编写和测试的一个快速而又脏的Roslyn示例。从NuGet安装Roslyn后,应该可以正常工作。脚本引擎初始化时的小膨胀可以很容易地包含在辅助类或方法中。

关键是传递HostObject。它可以是任何东西。完成后,您的脚本将拥有对属性的完全访问权限。 请注意,您只需在脚本中调用属性而不是主机对象。

基本上,您的主机对象将包含脚本所需的数据属性。不必将主机对象视为单个数据对象,而是将其视为配置。

public class MyHostObject
{
    public string Value1 { get; set; }
    public string Value2 { get; set; }
}

public class RoslynTest
{
    public void Test()
    {
        var myHostObject = new MyHostObject
        {
            Value1 = "Testing Value 1",
            Value2 = "This is Value 2"
        };

        var engine = new ScriptEngine();
        var session = engine.CreateSession(myHostObject);
        session.AddReference(myHostObject.GetType().Assembly.Location);
        session.AddReference("System");
        session.AddReference("System.Core");
        session.ImportNamespace("System");

        // "Execute" our method so we can call it.
        session.Execute("public string UpdateHostObject() { Value1 = \"V1\"; Value2 = \"V2\"; return Value1 + Value2;}");

        var s = session.Execute<string>("UpdateHostObject()");

        //s will return "V1V2" and your instance of myHostObject was also changed.
    }

}

答案 2 :(得分:-1)

没有。你不能。 至少以任何简单的方式。 你想要的东西就像是来自javascript的eval('do something')。 这与C#无法做到。 C#是一种在执行之前需要编译的语言,不像javascript(例如)。

实现它的唯一方法是构建自己的(非常复杂的初学者)解析器并以这种方式执行它。

<强>更新:

实际上,正如JDB公认的那样,这真的不是唯一的方法。我喜欢编程!有很多方法可以制作一个怪异的(有时甚至是某些自定义有趣的任务(甚至是学习)真正需要的!)代码。他他

我想到的另一种方法是构建一些.cs文件,然后即时编译它并将其作为一些程序集或其他模块使用。右。