我尝试使用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();
.
.
.
}
但我不想要一个对象的静态实例。我只想传递一个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();
}
更新
我发现quake-console
with a Roslyn option on Github提供AddVariable
方法,但对于我尝试做的事情,DirectX和MonoGame等似乎非常沉重......如果有这样的话Mono.CSharp中的某种方法......
答案 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();
}