Mono.CSharp:如何将值/实体*注入*脚本? [英] Mono.CSharp: how do I inject a value/entity *into* a script?
问题描述
刚刚接触到最新版本的 Mono.CSharp,并喜欢它提供的承诺.
Just came across the latest build of Mono.CSharp and love the promise it offers.
能够解决以下所有问题:
Was able to get the following all worked out:
namespace XAct.Spikes.Duo
{
class Program
{
static void Main(string[] args)
{
CompilerSettings compilerSettings = new CompilerSettings();
compilerSettings.LoadDefaultReferences = true;
Report report = new Report(new Mono.CSharp.ConsoleReportPrinter());
Mono.CSharp.Evaluator e;
e= new Evaluator(compilerSettings, report);
//IMPORTANT:This has to be put before you include references to any assemblies
//our you;ll get a stream of errors:
e.Run("using System;");
//IMPORTANT:You have to reference the assemblies your code references...
//...including this one:
e.Run("using XAct.Spikes.Duo;");
//Go crazy -- although that takes time:
//foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
//{
// e.ReferenceAssembly(assembly);
//}
//More appropriate in most cases:
e.ReferenceAssembly((typeof(A).Assembly));
//Exception due to no semicolon
//e.Run("var a = 1+3");
//Doesn't set anything:
//e.Run("a = 1+3;");
//Works:
//e.ReferenceAssembly(typeof(A).Assembly);
e.Run("var a = 1+3;");
e.Run("A x = new A{Name="Joe"};");
var a = e.Evaluate("a;");
var x = e.Evaluate("x;");
//Not extremely useful:
string check = e.GetVars();
//Note that you have to type it:
Console.WriteLine(((A) x).Name);
e = new Evaluator(compilerSettings, report);
var b = e.Evaluate("a;");
}
}
public class A
{
public string Name { get; set; }
}
}
这很有趣...可以在脚本的范围内创建一个变量,然后导出该值.
And that was fun...can create a variable in the script's scope, and export the value.
只有最后一件事要弄清楚...如何在不使用静态的情况下(例如,我想在其上应用规则脚本的域实体)获取值(我正在考虑在网络应用)?
There's just one last thing to figure out... how can I get a value in (eg, a domain entity that I want to apply a Rule script on), without using a static (am thinking of using this in a web app)?
我见过使用编译的委托——但那是用于以前版本的 Mono.CSharp,它似乎不再工作了.
I've seen the use compiled delegates -- but that was for the previous version of Mono.CSharp, and it doesn't seem to work any longer.
有人对如何使用当前版本提出建议吗?
Anybody have a suggestion on how to do this with the current version?
非常感谢.
参考资料:* 将变量注入Mono.CSharp.Evaluator(从字符串编译 LINQ 查询的运行时)* http://naveensrinivasan.com/tag/mono/
推荐答案
我知道已经快 9 年了,但我想我找到了一个可行的解决方案来注入局部变量.它使用的是静态变量,但仍然可以被多个评估者使用而不会发生冲突.
I know it's almost 9 years later, but I think I found a viable solution to inject local variables. It is using a static variable but can still be used by multiple evaluators without collision.
您可以使用静态 Dictionary
来保存要注入的引用.假设我们在我们的类 CsharpConsole
中完成所有这些工作:
You can use a static Dictionary<string, object>
which holds the reference to be injected. Let's say we are doing all this from within our class CsharpConsole
:
public class CsharpConsole {
public static Dictionary<string, object> InjectionRepository {get; set; } = new Dictionary<string, object>();
}
这个想法是暂时将值放置在其中,并以 GUID 作为键,这样多个评估器实例之间就不会发生任何冲突.要注入这样做:
The idea is to temporarily place the value in there with a GUID as key so there won't be any conflict between multiple evaluator instances. To inject do this:
public void InjectLocal(string name, object value, string type=null) {
var id = Guid.NewGuid().ToString();
InjectionRepository[id] = value;
type = type ?? value.GetType().FullName;
// note for generic or nested types value.GetType().FullName won't return a compilable type string, so you have to set the type parameter manually
var success = _evaluator.Run($"var {name} = ({type})MyNamespace.CsharpConsole.InjectionRepository["{id}"];");
// clean it up to avoid memory leak
InjectionRepository.Remove(id);
}
此外,对于访问局部变量,还有一种使用反射的解决方法,因此您可以使用 get 和 set 获得一个不错的 [] 访问器:
Also for accessing local variables there is a workaround using Reflection so you can have a nice [] accessor with get and set:
public object this[string variable]
{
get
{
FieldInfo fieldInfo = typeof(Evaluator).GetField("fields", BindingFlags.NonPublic | BindingFlags.Instance);
if (fieldInfo != null)
{
var fields = fieldInfo.GetValue(_evaluator) as Dictionary<string, Tuple<FieldSpec, FieldInfo>>;
if (fields != null)
{
if (fields.TryGetValue(variable, out var tuple) && tuple != null)
{
var value = tuple.Item2.GetValue(_evaluator);
return value;
}
}
}
return null;
}
set
{
InjectLocal(variable, value);
}
}
使用这个技巧,您甚至可以注入您的评估代码可以从脚本中调用的委托和函数.例如,我注入了一个打印函数,我的代码可以调用它来向 gui 控制台窗口输出一些内容:
Using this trick, you can even inject delegates and functions that your evaluated code can call from within the script. For instance, I inject a print function which my code can call to ouput something to the gui console window:
public delegate void PrintFunc(params object[] o);
public void puts(params object[] o)
{
// call the OnPrint event to redirect the output to gui console
if (OnPrint!=null)
OnPrint(string.Join("", o.Select(x => (x ?? "null").ToString() + "
").ToArray()));
}
这个 puts
函数现在可以像这样轻松注入:
This puts
function can now be easily injected like this:
InjectLocal("puts", (PrintFunc)puts, "CsInterpreter2.PrintFunc");
只需从您的脚本中调用:
And just be called from within your scripts:
puts(new object[] { "hello", "world!" });
注意,还有一个原生函数 print
,但它直接写入 STDOUT 并且无法从多个控制台窗口重定向单个输出.
Note, there is also a native function print
but it directly writes to STDOUT and redirecting individual output from multiple console windows is not possible.
这篇关于Mono.CSharp:如何将值/实体*注入*脚本?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!