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

是否可以执行以字符串表示的C#代码?,c#,string,interpreter,C#,String,Interpreter,在我的表格上,我点击了一个按钮 private void button1_Click(object sender, EventArgs e) { do something } 如何从文本文件中加载我的“做某事”,例如,我的文本文件如下所示: MessageBox.Show("hello"); label1.Text = "Hello"; string src = @" namespace x { using System.

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

private void button1_Click(object sender, EventArgs e)
{
    do something                           
}
如何从文本文件中加载我的“做某事”,例如,我的文本文件如下所示:

MessageBox.Show("hello");
label1.Text = "Hello";
string src = @"
namespace x
{
    using System.Windows;
    public class y
    {
        public void z(Label label1)
        {
            MessageBox.Show(""hello"");
            label1.Text = ""Hello"";
        }
    }
}
";
如果可能的话,单击它可以完成我的文本文件中的所有操作。

不,您不能。 至少以任何简单的方式。 您需要的是类似于javascript中的
eval('do something')
。 这与C#是不可能的。C#是一种在执行之前需要编译的语言,与javascript(例如)不同

实现这一点的唯一方法是构建自己的(对于初学者来说相当复杂)解析器并以这种方式执行它

更新:

实际上,正如JDB所注意到的,这并不是唯一的方法。我喜欢编程!有很多方法可以制作一个奇怪的代码(甚至有时对于一些定制的有趣的任务(甚至学习)来说确实是必要的!)。呵呵

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

不,你不能。 至少以任何简单的方式。 您需要的是类似于javascript中的
eval('do something')
。 这与C#是不可能的。C#是一种在执行之前需要编译的语言,与javascript(例如)不同

实现这一点的唯一方法是构建自己的(对于初学者来说相当复杂)解析器并以这种方式执行它

更新:

实际上,正如JDB所注意到的,这并不是唯一的方法。我喜欢编程!有很多方法可以制作一个奇怪的代码(甚至有时对于一些定制的有趣的任务(甚至学习)来说确实是必要的!)。呵呵


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

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

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
),而我上面的回答显然没有提供这一点。我能想到的最好的解决方案是一个有限的解决方案,它要求依赖项在“脚本”中显式地指定为参数。例如,编写如下脚本代码:

MessageBox.Show("hello");
label1.Text = "Hello";
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());
}
var result=provider.compileAsemblyFromSource(新编译器参数(),src);
if(result.Errors.Count==0)
{
var type=result.CompiledAssembly.GetType(“x.y”);
var instance=Activator.CreateInstance(类型);
var方法=type.GetMethod(“z”);
var args=新列表();
//假设任何参数都是当前对象的属性/字段
foreach(方法.GetParameters()中的var p)
{
var prop=this.GetType().GetProperty(p.Name);
var field=this.GetType().GetField(p.Name);
如果(prop!=null)
Add(prop.GetValue(this,null));
else if(字段!=null);
Add(field.GetValue(this));
其他的
抛出新的InvalidOperationException(“未找到参数“+p.Name+”);
}
调用(实例,args.ToArray());
}

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

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
),而我上面的回答显然没有提供这一点。我能想到的最好的解决方案是一个有限的解决方案,它要求依赖项在“脚本”中显式地指定为参数。例如,编写如下脚本代码:

MessageBox.Show("hello");
label1.Text = "Hello";
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());
}
var result=provider.compileAsemblyFromSource(新编译器参数(),src);
if(result.Errors.Count==0)
{
var type=result.CompiledAssembly.GetType(“x.y”);
var instance=Activator.CreateInstance(类型);
var方法=type.GetMethod(“z”);
var args=新列表();
//假设任何参数都是当前对象的属性/字段
foreach(方法.GetParameters()中的var p)
{
var prop=this.GetType().GetProperty(p.Name);
var field=this.GetType().GetField(p.Name);
如果(prop!=null)
Add(prop.GetValue(this,null));
else if(字段!=null);
Add(field.GetValue(this));
其他的
抛出新的InvalidOperationException(“未找到参数“+p.Name+”);
}
调用(实例,args.ToArray());
}

正如其他答案所述,这不是一件容易实现的事情,可能通过反射来实现,具体取决于脚本的高级程度

但是没有人@BrankoDimitrijevic提到罗斯林,这是一个很好的工具

它已经有一段时间没有更新了(2012年9月),并且没有实现C#的所有功能,但是,当我使用这个版本时,它确实实现了很多功能

通过将程序集添加为脚本会话的引用,您可以访问程序集的所有类型并针对它们编写脚本。它还支持返回值,因此您可以返回脚本方法生成的任何数据

下面是我刚刚编写和测试的Roslyn的一个快速而肮脏的例子。在从NuGet安装Roslyn后,应该可以立即使用。脚本引擎初始化时的小膨胀可以很容易地包装在h中