⚙️

DynamicExpressoを使用して文字列からメソッドを実行する

に公開

はじめに

私は極度のめんどくさがり屋なので、コードは極力書きたくないと思い、、設定ファイルなどに存在する文字列から関数を呼び出せるようなパッケージやライブラリは存在しないか、と探し始めたのことが発端です。
特に、文字列からメソッド名を指定してメソッドを実行させたい、、という切望をかなえるべく探していたところ、DynamicExpressoに出会い、日頃の感謝もこめてご紹介といたします。
(リフレクションをがんばれば実現可能そう、と把握してましたが、もうちょっと整理されたものがあれば、、と持病のめんどくさがり屋がでてしまいました。)

DynamicExpressoとは

https://github.com/dynamicexpresso/DynamicExpresso

Dynamic Expresso は、.NET Standard 2.0 で記述された単純な C# ステートメントのインタプリタです。 Dynamic Expressoは独自の解析ロジックを埋め込み、C#ステートメントを.NETラムダ式またはデリゲートに変換することで実際に解釈します。
Dynamic Expressoを使用すると、開発者はスクリプト可能なアプリケーションを作成したり、コンパイルせずに.NETコードを実行したり、動的なlinqステートメントを作成したりできます。
ステートメントは、C# 言語仕様のサブセットを使用して記述されます。グローバル変数またはパラメータは、式内で挿入して使用できます。アセンブリは生成されませんが、式ツリーをその場で作成します。

とのことで、私の偏見も含めてざっくりまとめますと、文字列から計算式や関数を呼び出せます。すごすぎ泣いた。
Nugetからインストール可能です。
.NET Standard 2.0と記載されていますが、私の環境では.NET 8で実行しており、特に問題は発生していません。

Web Shellで実行できるデモページが提供されてますので、こちらで試すこともできます。
https://dynamic-expresso.azurewebsites.net/

使い方

基本的な使い方は、DynamicExpresso製作者様のgithub readmeページにありますので、記載しません。私が使用した例をもとに記載します。

静的メソッドの実行

前提条件

MyAssemblyというプロジェクトに、以下のような静的クラスがあるとします。

namespace MyAssembly;
public static class MyClass
{
    public static void MyActMethod()
    {
        Console.WriteLine($"MyActMethod_Test");
        return;
    }
    public static int MyFuncMethod(int val1 , int val2)
    {
        Console.WriteLine($"MyFuncMethod_Test");
        return val1 + val2;
    }
}

実行準備

DynamicExpressoのInterpreterクラスをインスタンス化して、アセンブリ情報やクラス情報をわからせます。

string className = "MyAssembly.MyClass";
Type? type = Type.GetType(className);
// DynamicExpressoのInterpreterクラスをインスタンス化
Interpreter interpreter = new();
// クラス情報をわからせ
interpreter.Reference(type);

voidメソッドの実行

単純なvoidメソッドを実行させます。

//実行したい文字列コード
string functionString = "MyClass.MyActMethod();";
// DynamicExpressoのEvalで実行
object result = interpreter.Eval(functionString);

実行結果

functionメソッドの実行

引数あり、戻り値ありのfunctionメソッドを実行させます。

//実行したい文字列コード
string functionString = "MyClass.MyFuncMethod(p1,p2);";
//メソッドの引数を渡すためのDynamicExpressoのParameterを準備します。
List<Parameter> param = [];
param.Add(new Parameter("p1", typeof(int), 1));
param.Add(new Parameter("p2", typeof(int), 2));
// DynamicExpressoのEvalで実行
object result = interpreter.Eval(functionString, param.ToArray());
Console.WriteLine($"{result}");

実行結果

メンバメソッド実行

インスタンスクラスの場合は、インスタンス化したクラスオブジェクトをDynamicExpressoにわからせてから実行します。

前提条件

以下のインスタンスクラスがあるとします。

namespace MyAssembly;
public class MyInstanceClass
{
    public void MyInstanceActMethod()
    {
        Console.WriteLine($"MyInstanceActMethod_Test");
        return;
    }
    public int MyInstanceFuncMethod(int val1 , int val2)
    {
        Console.WriteLine($"MyInstanceFuncMethod_Test");
        return val1 + val2;
    }
}

インスタンスクラスのメンバメソッド実行

// MyInstanceClass のインスタンスを作成
MyInstanceClass myInstance = new();
// DynamicExpressoのInterpreterクラスをインスタンス化
Interpreter interpreter = new();
// DynamicExpressoのInterpreterにmyInstanceを変数としてわからせ
interpreter.SetVariable("obj", myInstance);

// MyInstanceActMethodを呼び出す。
interpreter.Eval("obj.MyInstanceActMethod()");

// 引数用パラメータを準備する
List<Parameter> param = [];
param.Add(new Parameter("p1", typeof(int), 3));
param.Add(new Parameter("p2", typeof(int), 6));
// MyInstanceFuncMethodを呼び出す。
object result = interpreter.Eval("obj.MyInstanceFuncMethod(p1,p2)", param.ToArray());
Console.WriteLine($"{result}");

実行結果

まとめ

DynamicExpressoを使用した簡単なメソッド実行方法を検証しました。
個人的には、他いろんな用途で使っていけるのでは?、と思い、DynamicExpressoの紹介でした。
製作者様に感謝です。

Discussion