【问题标题】:Mono.CSharp: how do I inject a value/entity *into* a script?Mono.CSharp:如何将值/实体*注入*脚本?
【发布时间】:2012-07-19 03:06:51
【问题描述】:

刚刚接触到最新版本的 Mono.CSharp,并喜欢它提供的承诺。

能够解决以下所有问题:

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; }
}
}

这很有趣...可以在脚本的范围内创建一个变量,然后导出该值。

只有最后一件事要弄清楚...我如何在不使用静态的情况下(例如,我想在其上应用规则脚本的域实体)获取值(我正在考虑在网络应用)?

我见过使用编译的委托——但那是针对以前版本的 Mono.CSharp 的,它似乎不再工作了。

有人对如何使用当前版本提出建议吗?

非常感谢。

参考资料: * Injecting a variable into the Mono.CSharp.Evaluator (runtime compiling a LINQ query from string) * http://naveensrinivasan.com/tag/mono/

【问题讨论】:

    标签: mono


    【解决方案1】:

    我知道已经快 9 年了,但我认为我找到了注入局部变量的可行解决方案。它使用的是静态变量,但仍然可以被多个评估者使用而不会发生冲突。

    您可以使用静态Dictionary<string, object> 来保存要注入的引用。假设我们在我们的班级 CsharpConsole 内完成所有这些工作:

    public class CsharpConsole {
       public static Dictionary<string, object> InjectionRepository {get; set; } = new Dictionary<string, object>();
    }
    

    这个想法是暂时将值放置在其中,并以 GUID 作为键,这样多个评估器实例之间就不会发生任何冲突。要注入这样做:

    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 获得一个不错的 [] 访问器:

            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 控制台窗口输出一些内容:

            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() + "\n").ToArray()));
            }
    

    这个puts 函数现在可以像这样轻松注入:

       InjectLocal("puts", (PrintFunc)puts, "CsInterpreter2.PrintFunc");
    

    只需从您的脚本中调用:

       puts(new object[] { "hello", "world!" });
    

    注意,还有一个本机函数 print,但它直接写入 STDOUT 并且无法从多个控制台窗口重定向单个输出。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-02
      • 1970-01-01
      • 1970-01-01
      • 2021-10-25
      • 2016-01-15
      • 1970-01-01
      • 2011-12-29
      • 1970-01-01
      相关资源
      最近更新 更多