是否可以执行以字符串表示的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中