我可以使用Mono.CSharp.dll来评估类实例上的表达式吗?

时间:2016-09-29 12:54:26

标签: c# .net mono

我尝试使用Mono.CSharp NuGet包来评估类实例上的表达式。我在this answer上看到了this question,但它没有具体说明我是否能够将它用于实例,只是它可以评估一般表达式。

这是我对评估者代码的包装:

public class Foo
{
    private Evaluator _evaluator;
    private StringBuilder _errors;

    public Foo()
    {

        this._errors = new StringBuilder();
        var tw = new StringWriter(this._errors);

        var ctx = new CompilerContext(new CompilerSettings()
        {
            AssemblyReferences = new List< string>
            {
                Assembly.GetExecutingAssembly().FullName
            }

        }, new StreamReportPrinter(tw));

        var eval = new Evaluator(ctx)
        {
            InteractiveBaseClass = typeof( Bar ) //Do I need this???? I don't know what it does...
        };


        string usings = @"
        using System;
        using System.Drawing; 
        using System.Collections.Generic;
        using System.Linq; ";

        eval.Run(usings);
        this._evaluator = eval;
    }

    public string Evaluate(string expression)
    {
        try
        {
            object result;
            bool results;


            this._errors.Clear();

            this._evaluator.Evaluate(expression, out result, out results);

            if ( results )
            {
                return result.ToString();
            }

            if ( this._errors.Length > 0 )
            {
                return this._errors.ToString();
            }
        }
        catch ( Exception ex )
        {
            return ex.Message;
        }
        return "";
    }
}

这是我想要获得的属性/字段的对象:

public class Bar
{
    public string Joke = "How do you say 3 cats drown in French?";
    public string Punchline => "Trois Cats sank!";
    public string Drumroll = "Ba dum tsss...";
}

我使用一个简单的控制台应用程序来使用包装器:

private static void Main(string[] args)
{
    var foo = new Foo();
    string input = Console.ReadLine();

    while (!string.IsNullOrEmpty(input)) 
    {
        Console.WriteLine(foo.Evaluate(input));
        input = Console.ReadLine();
    }

    Console.WriteLine("Press any key to quit");
    Console.Read();
}

如果我创建public static类的Bar实例,我可以从控制台查看其属性:

public static Bar b;
private static void Main(string[] args)
{
    b = new Bar();
    .        
    .        
    .        
}

enter image description here

但我不想要一个对象的静态实例。我只想传递一个pre-instanced对象并使用它的属性。

如果我在本地(或在类级别)实例化对象并尝试对其进行评估,则结果如下:

private static void Main(string[] args)
{
    var b = new Bar();

    var foo = new Foo();
    string input = Console.ReadLine();

    while (!string.IsNullOrEmpty(input)) 
    {
        Console.WriteLine(foo.Evaluate(input));
        input = Console.ReadLine();
    }

    Console.WriteLine("Press any key to quit");
    Console.Read();
}

enter image description here

更新

我发现quake-console with a Roslyn option on Github提供AddVariable方法,但对于我尝试做的事情,DirectX和MonoGame等似乎非常沉重......如果有这样的话Mono.CSharp中的某种方法......

1 个答案:

答案 0 :(得分:1)

不确定你的意思是什么实例对象 - 你提供的代码中没有任何Bar实例(我认为)。但是,您可以评估实例对象的属性,但需要先创建它。运行您的控制台应用程序并键入此(在控制台中):

var b = new ConsoleApplication1.Bar();

ConsoleApplication1是Bar类的命名空间。现在,您可以计算此实例变量的表达式:

b.Joke // outputs How do you say 3 cats drown in French?

如果您不希望用户创建此对象 - 只需计算字符串“var b = new ConsoleApplication1.Bar();”在Console.ReadLine()陈述之前,你自己。

更新。可以将外部变量导入到赋值器的范围内,但这需要一些反思,因为API设计者由于某种原因没有公开它。请参阅以下代码:

public class Foo
{
    private Evaluator _evaluator;
    private StringBuilder _errors;

    public Foo(object context, string contextName) {

        this._errors = new StringBuilder();
        var tw = new StringWriter(this._errors);

        var ctx = new CompilerContext(new CompilerSettings() {
            AssemblyReferences = new List<string> {
                Assembly.GetExecutingAssembly().FullName
            }

        }, new StreamReportPrinter(tw));
        var eval = new Evaluator(ctx);
        string usings = @"
    using System;
    using System.Drawing; 
    using System.Collections.Generic;
    using System.Linq; ";
        eval.Run(usings);

        object result;
        bool results;
        // here we initialize our variable, but set it to null
        var constructor = $"{context.GetType().FullName} {contextName} = null;";
        eval.Evaluate(constructor, out result, out results);
        // here we use reflection to get private field which stores information about available variables
        FieldInfo fieldInfo = typeof(Evaluator).GetField("fields", BindingFlags.NonPublic | BindingFlags.Instance);
        var fields = (Dictionary<string, Tuple<FieldSpec, FieldInfo>>) fieldInfo.GetValue(eval);
        // and we set variable we just created above to the "context" external object
        fields[contextName].Item2.SetValue(eval, context);

        this._evaluator = eval;
    }

    public string Evaluate(string expression)
    {
        try
        {
            object result;
            bool results;
            this._errors.Clear();

            this._evaluator.Evaluate(expression, out result, out results);

            if (results)
            {
                return result.ToString();
            }

            if (this._errors.Length > 0)
            {
                return this._errors.ToString();
            }
        }
        catch (Exception ex)
        {
            return ex.Message;
        }
        return "";
    }
}

然后在您的Main方法中,传递您的实例“Bar”对象,如下所示:

private static void Main(string[] args)
{
        var bar = new Bar();
        // here you define the name of new variable ("b")
        var foo = new Foo(bar, "b");
        string input = Console.ReadLine();

        while (!string.IsNullOrEmpty(input))
        {
            Console.WriteLine(foo.Evaluate(input));
            input = Console.ReadLine();
        }

        Console.WriteLine("Press any key to quit");
        Console.Read();
}