Edited at

C#のExpressionで動的コード生成

More than 1 year has passed since last update.


はじめに

この記事ではC#のExpressionという最高にナウでヤングでイケてるライブラリについて

説明するぜ。

もし、間違ったところがあれば指摘するんだぜ。


Expressionとは?

まず、Expressionとは、式木を組み立て、コードを動的に生成することができるライブラリだぜ。

この機能を使うことで、関数電卓を作ったり、簡単なコンパイラなんかを作ることができるぜ。


式木とは?

式木はその名の通り、式の木なんだぜ。

例えば、4+5はSY5Ts.png

となるのぜ。

もっと複雑な式木を考えてみるぜ。

4+2*3

SY5Ts.png


Expression実践

じゃあ早速Expressionで4+5の式木を組み立てて見るのぜ。

Expressionはさまざまな種類があるんだが、それらは全てExpressionを継承してるんだぜ。

各種Expressionは、Expressionで定義されているstaticメソッドを使って作成するのぜ。

あっちなみにVisual Studio 2015で動作確認済みだぜ。

using System.Linq.Expressions;

namespace ExpressionQiita
{
class Program
{
static void Main(string[] args)
{
var expr=
Expression.Add(
Expression.Constant(4),
Expression.Constant(5)
);
}
}
}

やったぜ!これで、4+5の式木が組み立てられたぜ!

じゃあ、新しく出てきた2つの関数について説明するぜ。

まずはExpression.Addの定義を紹介するぜ。

BinaryExpression Add(

Expression left,
Expression right
)

ほらわかるだろ?

こいつは、式を二つ受け取り、+演算を行う式を生成するのぜ。

BinaryExpressionは二項演算子を表す式のオブジェクトだぜ。

次にExpression.Constantの定義。

ConstantExpression Constant(

object value
)

こいつは定数を表す式を表現できるのぜ。

例えば、

Expression.Constant(40);

で定数40を表現できるのぜ。


実際に実行してみよう

じゃあ、式木も作ったし、動的コード生成して、4+5の結果を取得してみよう。

残念、このままじゃ、まだだめなんだぜ。

式木はデリゲートとしてコードを生成するんだ。

つまり、関数だ。

4+5は関数じゃないから生成できないんだぜ。

じゃあ、どうするのか。

まったく簡単!

ラムダ式で包み込むのぜ!

ラムダ式は式だからね!ひゃはー!もちろん式木の仲間さ!

こうなるのぜ ()=>4+5

ラムダ式の構文がわからない?そんなもんしらんわ。

修正したコードはこうだぜ。

using System.Linq.Expressions;

using System;
namespace ExpressionQiita
{
class Program
{
static void Main(string[] args)
{
var expr =
//引数なし、返り値がintのラムダ式を表す
Expression.Lambda<Func<int>>(
Expression.Add(
Expression.Constant(4),
Expression.Constant(5)
)
);

//動的コード生成
Func<int> func=expr.Compile();

//実行してみる
Console.WriteLine(func());
}
}
}

新しく出てきたExpression.Lambdaの定義はこうだぜ。

LambdaExpression Lambda(

Expression body,
params ParameterExpression[] parameters
)

Expression.Lambda(ラムダ式に含めたい式,ラムダ式の引数)

てかんじだぜ

まあラムダ式の引数についてはすぐ後に説明するぜ。

今回は引数はとらないから省略だぜ。

このコードを実行すると()=>4+5というラムダ式を動的コード生成して、

funcに保存しているぜ。

あとはfuncはふつーにデリゲートとして使えるんだぜ。

そのあと、Console.WriteLine(func())でfuncの計算結果 9を表示しているぜ。


引数を渡そう!

毎回毎回4+5の結果を表示するだけの関数なんていらねー!

というわけで、(int x,int y)=>x+yな関数を生成するぜ。

using System.Linq.Expressions;

using System;
namespace ExpressionQiita
{
class Program
{
static void Main(string[] args)
{
var x = Expression.Parameter(typeof(int),"x");
var y = Expression.Parameter(typeof(int));
var expr =
//引数なし、返り値がintのラムダ式を表す
Expression.Lambda<Func<int,int,int>>(
Expression.Add(
x,
y
),x,y
);

//動的コード生成
Func<int,int,int> func=expr.Compile();

//実行してみる
Console.WriteLine(func(4,6));
}
}
}

Expression.Lambda関数の第二、第三には、引数を設定してるのぜ。

こうすることで、Int型の引数を2つ受け取り、x,yに代入できるぜ。

今回の例だと、4と6を引数x,yとして受け取り、計算しているぜ。

【需要があったら続きかきます】