从字符串评估嵌套属性调用

时间:2016-01-01 22:27:07

标签: c# python reflection roslyn

我目前正在阅读" 500行或更少" bookchapter,用于从Ned Batchelder创建模板引擎。

他们的例子是使用Python。在他们的模板引擎中,他们将代码构建为字符串,然后他们调用execdocs)来将字符串计算为Python代码。

def get_globals(self):
    """Execute the code, and return a dict of globals it defines."""
    # A check that the caller really finished all the blocks they started.
    assert self.indent_level == 0
    # Get the Python source as a single string.
    python_source = str(self)
    # Execute the source, defining globals, and return them.
    global_namespace = {}
    exec(python_source, global_namespace)
    return global_namespace

这非常方便,因为他们可以轻松评估模板中的表达式,例如{{object.property.property}}

以C#作为我的主要编程语言,我想知道如何实现这一点(在本书中构建模板引擎的上下文中)?

研究与思考

首先,我不相信C#中有exec个等价物。

我能想到的一种方法是递归地使用Reflection来获取对象的属性列表(处理Null引用的检查),但从性能的角度来看,我不喜欢这样。

另一种方法是使用Roslyn的ScriptEngine课程(如果我错了,我还没有使用过,所以请纠正我)。但我担心这不会很好,因为这应该是一个库,它不能用于旧版本的C#和.NET。 Example

2 个答案:

答案 0 :(得分:2)

  

问:首先我不相信C#中有一个exec等价物。

至于编译C#代码,可以使用CS-Script库以各种方式实现此目的。

例如:

dynamic script = CSScript.Evaluator
                         .LoadCode(@"using System;
                                     using Your.Custom.Relevant.Namespace;
                                     public class Executer
                                     {
                                         public object Execute()
                                         {
                                             return SomeStaticClass.array[123];
                                         }
                                     }");
int result = script.Execute();

//shorter way
int a = (int)CSScript.Evaluator.Evaluate("some.namespace.SomeStaticClass.array[123]");

在此处阅读更多内容:http://www.csscript.net/

CS-Script 不是用于模板化 除非你在编译之前通过操纵字符串来自己创建它。

  

但是如何为模板引擎传递一些上下文

您可以将上下文传递给这样的函数:

dynamic script = CSScript.Evaluator
                     .LoadCode(@"
                                using System;
                                using Namespace.Of.The.Context;
                                public class Executer {
                                    public string Execute(Context ctx) {
                                        return ctx.Person.Firstname + ctx.Person.Lastname;
                                    }
                                }");
int result = script.Execute(new Context(new Person("Rick", "Roll")));
  问:我可以从普通的C#应用​​程序调用CSScript吗?可以说是Web应用程序吗?

     <答>答:是的。

     

S-Script目前的目标是Microsoft实现CLR(.NET   2.0 / 3.0 / 3.5 / 4.0 / 4.5)完全支持Mono。

基本上如果它运行C#,它可以相应地编译到执行库的.net框架,所以如果您的项目是在.net4.5上运行的,那么.net版本的任何功能都可用,包括任何项目中的外部参考。

答案 1 :(得分:1)

您可以使用 static void Main(string[] args) { string source = @" namespace Test { public class Test { public void HelloWorld() { System.Console.WriteLine(""Hello World""); } } } "; var options = new Dictionary<string, string> { {"CompilerVersion", "v3.5"} }; var provider = new CSharpCodeProvider(options); var compilerParams = new CompilerParameters{GenerateInMemory = true, GenerateExecutable = false }; var results = provider.CompileAssemblyFromSource(compilerParams, source); var method = results.CompiledAssembly.CreateInstance("Test.Test"); var methodInfo = method.GetType().GetMethod("HelloWorld"); methodInfo.Invoke(method, null); } 来编译代码。

https://msdn.microsoft.com/en-us/library/microsoft.csharp.csharpcodeprovider.aspx

像这样:

def isThing(x, function, get_val):
    t = function(x)
    return get_val(t)

def getGrok(x):
    funcs = [(makeSnood, lambda s: s.snoodidle()), (makeTrund, lambda t: t.trundle()), (makeTooDeep, lambda d = d.groan())]
    grokster = None
    for func, get_val in funcs:
        try:  # is x referring to a snood?
            grokster = isThing(x, func, get_val)
            break
        except ValueError:
            continue

     if grokster:
         return grokster.grok()
     else:
         return None