Pass object instance to Roslyn ScriptEngine

人走茶凉 提交于 2019-11-30 03:35:07

问题


I'm looking for a C# scripting engine, that can interpret blocks of C# code, while maintaing a context. For example, if enter to it: var a = 1; , and then a + 3, it'll output 4. I'm aware of MS Roslyn , which indeed do that, but it's a sandbox (respect to the program that launched it). So, if I create an instance of ScriptEngine and an instance of MyClass (just an arbirary class of mine) , I have no option to pass a reference of my_class to script_engine.

Is it possible to somehow pass that reference?

What I'd like to do, is something like:

ScriptEngine engine; // A Roslyn object
Session session // A Roslyn object

MyClass my_class; // My object

// all required initializations

Submission<object> sm = session.CompileSubmission<object>("var a=1;"); 
dynamic result = sm.Execute(); 

Submission<object> sm = session.CompileSubmission<object>("a + 3;"); 
dynamic result = sm.Execute(); // result is now 4

MyClass my_class;
session.AddReferenceToAnOject(my_class); // function that does not exists, but reflect my intention

Submission<object> sm = session.CompileSubmission<object>("my_class.ToString();"); 
dynamic result = sm.Execute();  // result is no the output of my_class.ToString()

Please notice that AddReferenceToAnOject() is the missing part, as there's no such function in roslyn.


回答1:


The answer was found in a link commented by @Herman.

As it turn out, Roslyn ScriptEngine/Session supports a concept of Host Object. In order to use it, define a class of your choise, and pass it upon session creation. Doing so, makes all public member of that host object, available to context inside the session:

public class MyHostObject
{
    public List<int> list_of_ints;
    public int an_int = 23;
}

var hostObject = new MyHostObject();
hostObject.list_of_ints = new List<int>();
hostObject.list_of_ints.Add(2);
var engine = new ScriptEngine(new[] { hostObject.GetType().Assembly.Location });

// passing reference to hostObject upon session creation
var session = Session.Create(hostObject);

// prints `24` to console
engine.Execute(@"System.Console.WriteLine(an_int + list_of_ints.Count);", 
               session); 



回答2:


Here is a complete procedure how to pass variables from object to roslyn dynamic executed code. Included is also eval() wrapper. First install RoslynSetup.exe from microsoft.com http://www.microsoft.com/en-sa/download/details.aspx?id=34685

Then Add Reference Roslyn.Compilers and Roslyn.Compilers.CSharp to your project (Assemblies / extensions)

Here is full working code for C# console app in vs2012.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;
using Roslyn.Scripting;
using Roslyn.Scripting.CSharp;

namespace testRoslyn
{
    class Program
    {
        static void Main(string[] args)
        {
            TestRoslyn tr = new TestRoslyn();
            tr.Test = "this was set from main program ";
            tr.test();
            System.Console.WriteLine(tr.Test);
            tr.Test = "this was set from main program for eval";
            Eval e = new Eval();
            e.create<TestRoslyn>(tr);                           
            e.eval("Test = Test + \" AND THIS WAS SET FROM Eval()\";");
            System.Console.WriteLine(tr.Test);
            string a = e.eval<string>("string a = \"return this from eval\";a");
            System.Console.WriteLine(a);
            tr.Test = "now return this";
            string b = e.eval<string>("string a = Test + \" ... and this\";a");
            System.Console.WriteLine(b);
            double d = e.eval<double>("double dbl = 1.2345*3;dbl");
            System.Console.WriteLine(d);
            e.eval("string testIt(string a){return \"testIt(): \"+a+\"\";}");
            string c = e.eval<string>("string c = testIt(\"nice\");c");
            System.Console.WriteLine(c);
            Console.ReadKey();
        }
    }

    public class TestRoslyn
    {
        public string Test;

        public TestRoslyn()
        {
        }

        public string test()
        {
            ScriptEngine roslynEngine = new ScriptEngine();
            Roslyn.Scripting.Session session = roslynEngine.CreateSession(this);
            session.AddReference(this.GetType().Assembly);
            session.AddReference("System.Web");
            session.ImportNamespace("System");
            session.ImportNamespace("System.Web");
            var result = (string)session.Execute("Test = Test + \" ... and this was set from roslyn code.\";Test");
            return result;
        }
    }

    public class Eval
    {
        ScriptEngine RoslynEngine = new ScriptEngine();
        Roslyn.Scripting.Session Session;

        public void create<T>(T hostObject = null) where T : class
        {
            RoslynEngine = new ScriptEngine();
            Session = RoslynEngine.CreateSession(hostObject);
            if (hostObject != null)
                Session.AddReference(hostObject.GetType().Assembly);
            Session.AddReference("System.Web");
            Session.ImportNamespace("System");
            Session.ImportNamespace("System.Web");
        }

        public void eval (string strEval)
        {                                                
            Session.Execute(strEval);
        }

        public T eval<T>(string strEval) 
        {
            return (T) Session.Execute(strEval);            
        }
    }
}


来源:https://stackoverflow.com/questions/24733556/pass-object-instance-to-roslyn-scriptengine

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!