Roslyn for Scripting という機能を使うと、自作プログラムからC#で書いたプログラムを(ビルドせずに)即実行できるようになります。
今回は、その基本的な使い方について要点を解説したいと思います。
Roslyn for Scripting とは
Roslyn(ロズリン)とは、Visual Studio 2015で採用されたコンパイラープラットフォームのコードネームです。
コンパイラーに必要な機能(言語解析、シンタックハイライト、IntelliSense(プログラム補完機能)、リアルタイムエラー解析、リファクタリングなど)を網羅し、APIとして外部から呼び出されることを前提に開発されました。
Roslyn for Scriptin はこの中の1つの機能であり、C#言語の構文解析を行いながらリアルタイムで実行できる「スクリプト実行環境」を適用してくれます。
つまり、Roslyn for Scriptin を自作プログラムに組み込むことで、例えばテキストボックスに入力したC#プログラムをボタン1つで即実行し、結果を得ることができるようになります。

インストール方法
NuGetでプロジェクトにインストールします。
VisualStudioのメニューから「ツール」→「NuGetパッケージマネージャ」→「ソリューションのNuGetパッケージの管理」でNuGetのタブが表示されますので、”csharp script” というキーワードで検索してください。
似たようなものがいくつも表示されますが、「Microsoft.CodeAnalysis.CSharp.Scripting」を選択していただければOKです。
NuGetの使い方がわからない方は、こちらの記事をご覧ください。

Roslyn for Scripting の使い方
それでは、Roslyn for Scripting を使ってC#スクリプトを呼び出す方法について解説いたします。
Roslyn for Scripting には様々な機能がありますが、すべてを網羅することは困難なため、ここでは実用上最低限知っておく必要のある内容に限定しております。
C#スクリプトの実行
あらかじめ、次の参照設定を冒頭に記述しておきます。
1 2 |
using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; |
次に、CSharpScriptのCreateメソッドに実行したいC#スクリプトを渡してインスタンスを生成します。
そして、そのインスタンスの RunASyncメソッドを呼び出すことで、C#スクリプトが実行されます。
1 2 3 4 |
//実行したいスクリプトを渡してオブジェクトを生成 var script = CSharpScript.Create(script); //C#スクリプトを実行 var result = script.RunAsync().Result; |
C#スクリプトからの戻り値を受け取る
C#スクリプト側から呼び出し側に戻り値を返す場合、return を使います。
例えば、次のようなスクリプトを実行すると、戻り値として 300 が返されます。
1 2 3 |
int a = 100; int b = 200; return a + b; |
戻り値は RunAsync メソッドから返される ScriptState オブジェクトのResultValueプロパティで取得できます。
この時、受取側ではC#スクリプトが返す値の型に応じて適切にキャストする必要があります。
今回の例では a , b ともに int 型なので、次のようになります。
1 |
int val = (int)result.ReturnValue; |
もし型を間違えるとエラーになりますし、C#スクリプト側で return を記述し忘れた場合、ResultValue には null が返されるので注意が必要です。
受取時に型を気にしなくて済むようにするには、次のように文字列にキャストて受取り、必要に応じてTryPhaseで変換できるか試してみるというのが一番楽かと思います。
1 2 3 |
var script = CSharpScript.Create(script); var result = script.RunAsync().Result; string res = (result.ReturnValue == null) ? "" : result.ReturnValue.ToString(); |
スクリプトと様々な値を受け渡しする
C#スクリプトの実行時、任意のクラスを1つだけ引数として渡すことが可能です。
渡されたクラスのプロパティやメソッドは、C#スクリプトの中から自由にアクセスできます。
このクラスを通じて、C#スクリプトの呼び出し元とC#スクリプト内で様々なやり取りが可能です。
具体的には、CSharpScript.Create メソッドの引数にクラスの型を指定し、RunAsync メソッドの引数にクラスの実態(インスタンス)を指定します。

例えば、次のようなMyArgsクラスがあったとして、これをC#スクリプトに渡すことを考えてみます。
1 2 3 4 5 6 7 8 9 |
//C#スクリプトの引数として渡すクラス public class MyArgs { public int MyValue { get; set; } public int MyMethod(int value) { return value * value * value; } } |
まず最初に new MyArgs() でインスタンスを作成します。
次に、CSharpScript.Createメソッドの globalsTypeという引数名にtype(MyArgs)で型を指定し、RunAsyncメソッドの第一引数にインスタンスを指定すればOKです。
1 2 3 4 5 6 |
var args = new MyArgs(); args.MyValue = 100; var script = CSharpScript.Create(uxScript.Text, globalsType: typeof(MyArgs)); var result = script.RunAsync(args).Result; uxResult.Text = (result.ReturnValue == null) ? "" : result.ReturnValue.ToString(); |
C#スクリプト側で、次のようなスクリプトを実行すると、スクリプト終了後、args.MyValue の値が1000に書き換わっています。
1 |
MyValue = MyMethod(10); |
この様に、C#スクリプト側では、あたかも最初からMyValueやMyMethod が備わっていたかのように利用することが出来ます。
.NET標準のアセンブリ(DLL)をスクリプト上で利用できるようにする
初期設定の状態でC#スクリプトを実行すると、C#の基本的なクラスしかサポートされません。
例えば、DataTableや File、PathなどのIO関係のクラス、Dynamic型やLinq などもサポートされないのです。
これらを使いたい場合は、明示的にアセンブリ参照を行うような記述が必要です。
まず最初に次の参照設定を冒頭に記述しておきます。
1 |
using System.Reflection; |
手順は次の様になります。
- List<Assembly> assembly = new List<Assembly>(){アセンブリの列挙}
- List<string> import = new List<string>(){アセンブリ名の列挙}
- ScriptOptionsクラスのAddReferences、AddImportsメソッドで上記2つを登録
- CSharpScript.Create メソッドの引数にScriptOptionsのインスタンスを渡す
以下が実際のソースコードです。
このままコピペしていただければ、.NETで用意されている主なクラスは使えるようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
private void uxExec_Click(object sender, RoutedEventArgs e) { //アセンブリの読み込み List<Assembly> assembly = new List<Assembly>() { Assembly.GetAssembly(typeof(System.Dynamic.DynamicObject)), // System.Code Assembly.GetAssembly(typeof(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo)), // Microsoft.CSharp Assembly.GetAssembly(typeof(System.Dynamic.ExpandoObject)), Assembly.GetAssembly(typeof(System.Data.DataTable)) }; //C#スクリプトのインタプリタにインポートするアセンブリの指定 List<string> import = new List<string>() { "System","System.Dynamic", "System.Linq", "System.Text","System.IO", "System.Collections.Generic", "System.Data" }; //C#スクリプトのオプションパラメータにアセンブリとインポートのリストを登録 var option = ScriptOptions.Default.AddReferences(assembly).AddImports(import); var script = CSharpScript.Create(uxScript.Text,options: option); var result = script.RunAsync().Result; uxResult.Text = (result.ReturnValue == null) ? "" : result.ReturnValue.ToString(); } |
自作のアセンブリ(DLL)をスクリプト上で利用できるようにする
自作の共通クラスなどを作成していて、それをC#スクリプトから利用したい場合、Assembly.LoadFromを使ってDLLファイルのパスを指定します。
さらに、List<string>でもアセンブリ名を指定します。
1 2 3 4 5 6 7 8 9 10 |
List<Assembly> assembly = new List<Assembly>() { Assembly.LoadFrom("d:\MyCommon.dll")), Assembly.LoadFrom("d:\MyFileIO.dll")) }; List<string> import = new List<string>() { "MyCommon","MyFileIO" }; |
C#スクリプトの実行をメソッド化したサンプル
今までの解説をすべて盛り込んだ形で、C#スクリプトを簡単に実行できるメソッドをサンプルとして掲載しておきます。
C#スクリプト側でエラー(例外)が発生するとプログラムが落ちてしまうので、Try~Catch で例外を握りつぶしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public string ExecScript(string source, Type argstype, object args) { try { List<Assembly> assembly = new List<Assembly>() { Assembly.GetAssembly(typeof(System.Dynamic.DynamicObject)), // System.Code Assembly.GetAssembly(typeof(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo)), // Microsoft.CSharp Assembly.GetAssembly(typeof(System.Dynamic.ExpandoObject)), Assembly.GetAssembly(typeof(System.Data.DataTable)), }; List<string> import = new List<string>() { "System","System.Dynamic", "System.Linq", "System.Text","System.IO", "System.Collections.Generic", "System.Data" }; var opt = ScriptOptions.Default.AddReferences(assembly).AddImports(import); var script = CSharpScript.Create(source, globalsType: argstype, options: opt); var result = script.RunAsync(globals: args).Result; var value = result.ReturnValue; return (value == null) ? "" : value.ToString(); } catch { } return ""; } |
使い方は次の様になります。
1 2 3 4 5 6 7 8 |
var args = new MyArgs(); args.MyValue = 5; ExecScript(uxScript.Text, typeof(MyArgs), args); val res = args.MyValue; |
まとめ
Roslyn for Scripting を使うことで、自作プログラムの中からC#スクリプトを実行し、結果を得ることが出来るようになります。
C#スクリプトの呼び出し元とスクリプト内のデータの受け渡しは、任意のクラスを介して行うことが可能で、.NETで用意されている様々なクラスや、自作クラスでさえC#スクリプトから利用することが可能です。
非常に強力な機能ですので、是非ご活用ください。