メタプログラミング C#

50
メメメメメメメメメ C# メメ メメメ ROOM METRO #23 メメ 2014-03-01

description

Room metro #23 大阪 & Windows Phone Arch特別編 ~めとべやスペシャルサンクスDay~http://metrostyledev.net/index.php/event/20131026/ 2014年3月1日(土)

Transcript of メタプログラミング C#

Page 1: メタプログラミング C#

メタプログラミング C#

小島 富治雄ROOM METRO #23 大阪

2014-03-01

Page 2: メタプログラミング C#

自己紹介

• 小島 富治雄• @Fujiwo• 福井コンピュータアーキテクト株式会社

• Microsoft MVP C# (2005-2014)

Page 3: メタプログラミング C#

本日の資料

• スライドhttp://slidesha.re/1mMiXAz

• サンプル コードhttp://1drv.ms/NbF2fF• メタプログラミング入門 - 目次 - 翔ソフトウェア (Sho's)http://bit.ly/1hz6jFT

3

Page 4: メタプログラミング C#

• 2014/03/22 全国でオフライン セッション• 北陸会場 : 石川工業高等専門学校• 申し込み : http://atnd.org/events/47189

Page 5: メタプログラミング C#

本日のゴール

•C# でのメタプログラミングに興味を• 多くは Visual Basic でも可

Page 6: メタプログラミング C#

メタとは

• メタ (meta) は、「高次な-」「超-」等の意味の接頭語• ギリシャ語から

• プログラミングでは• メタプログラミング• メタクラス• クラスがインスタンスとなるクラス

• メタデータ• データが持つそのデータ自身についての付加的なデータ

Page 7: メタプログラミング C#

メタプログラミングとは

• 高次なプログラミング• プログラムを対象とするプログラムを書く

• プログラムを操作 / 出力するプログラムを書く• プログラムでプログラムを出力すると

手でプログラムを書くよりも効率的な場合が

Page 8: メタプログラミング C#

どんな問題を解こうとしてる ?

Page 9: メタプログラミング C#

DRY (Don't repeat yourself) の原則

• 繰り返しを避けたい• 似たようなプログラミングを繰り返し書きたくない

• オブジェクト指向プログラミングやジェネリック プログラミングだけでは解決できない繰り返しを解決• 状況に応じたパラダイムの使い分け

Page 10: メタプログラミング C#

実行時の環境に対応

• ビルド時でなく、実行時にプログラムを書きたい• 「クラスやメソッドはビルド時までに完成してない

といけない。実行時には追加 / 変更できない」• なんで ?

Page 11: メタプログラミング C#

メタプログラミングが有効な例

• コンパイラー / インタープリター• ホスト言語のソースコードから対象言語のプログラムを生成

• O/R マッパー• クラスやオブジェクトから SQL を生成

• モック (mock) オブジェクト• 「モック ( ユニットテストで用いられる代用のオブジェ

クト ) を生成」するプログラムを生成

Page 12: メタプログラミング C#

メタプログラミングが有効な例

• XML や JSON の入出力• 「クラスやオブジェクトから XML や JSON を生成 /

XML や JSON からクラスやオブジェクトを生成」するプログラムを生成

• Web アプリケーション• クライアント側で動作するプログラム

(JavaScript 等 ) をサーバー側で動的に生成

Page 13: メタプログラミング C#

メタプログラミングの例

Page 14: メタプログラミング C#

メタプログラミング C// C のマクロ (危険 )

#define SWAP(x, y, type) { type temporary__; temporary__ = x; x = y; y = temporary__; }

int main(void){    int a = 1;    int b = 2;

    SWAP(a, b, int);

    return 0;}

int main(void)

{

int a = 1;

int b = 2;

{

int temporary__;

temporary__ = a;

a = b;

b = temporary__;

}

return 0;

}

プリコンパイラー

Page 15: メタプログラミング C#

メタプログラミング C++

template <int N>

struct Factorial

{

enum { value = N *

Factorial<N - 1>::value };

};

template <>

struct Factorial<0>

{

enum { value = 1 };

};

int main()

{

int x = Factorial<3>::value;

return 0;

}

struct Factorial0 { enum { value = 1 }; };

struct Factorial1 { enum { value = 1 * Factorial0::value }; };

struct Factorial2 { enum { value = 2 * Factorial1::value }; };

struct Factorial3 { enum { value = 3 * Factorial2::value }; };

int main()

{

int x = Factorial3::value;

return 0;

}

コンパイラー

Page 16: メタプログラミング C#

C#/.NET におけるメタプログラミングの手段

• T4 (Text Template Transformation Toolkit) Template

• CodeDOM (Code Document Object Model)

•リフレクション (Emit)

•式木• Roslyn

Page 17: メタプログラミング C#

T4 (Text Template Transformation Toolkit) Template

• Visual Studio でコード生成• T4

テキスト テンプレートを使用したデザイン時コード生成 - MSDN

Page 18: メタプログラミング C#

CodeDOM (Code Document Object Model)

• System.CodeDom 名前空間や System.CodeDom.Compiler 名前空間• C# や Visual Basic 等のコードを生成• コンパイルしてアセンブリを生成

CodeDOM CodeDOMProvider

ソース コード(C# 、 VB 、 JScript

)

アセンブリ

GenerateCodeFromNamespace

CompileAssemblyFromDom

Page 19: メタプログラミング C#

CodeDOM: Demo

• Hello world! の動的生成namespace CodeDomHelloWorldDemo{ using System;

class Program { static void Main() { Console.WriteLine("Hello world!"); Console.ReadKey(); } }}

Page 20: メタプログラミング C#

リフレクション

• System.Reflection 名前空間• クラスやインスタンスに関する情報 ( メタデータ ) を取

得し、メンバーを呼び出したりできる

• System.Reflection.Emit 名前空間• CIL (Common Intermediate Language) からクラス等

を動的生成

Page 21: メタプログラミング C#

式木

• System.Linq.Expressions 名前空間• 式木を生成し、動的にプログラムを生成

Page 22: メタプログラミング C#

式木 Expression<Func<Employee, bool>> expression = employee => employee.Name.Contains(" 山 ");

Parameters

Body Object

Method

Arguments

Expression

Member

employee => employee.Name.Contains(" 山 ")

employee.Name

Contains

employee

Name

“ 山”

employee

employee.Name.Contains(" 山 ")

Page 23: メタプログラミング C#

Roslyn

• C# や Visual Basic のコンパイラーの内部の API 等を公開• "Compiler as a Service“

• C# や Visual Basic のソースコード解析機能• C# のソースコードから、プログラムを生成

Page 24: メタプログラミング C#

リフレクション : Demo

• 動的プログラム生成の前に「普通の」リフレクション• リフレクションによるデータバインド• 実行時に型のメタデータを取得

Assembly

Module

Type・ Class・ Interface・ Value Type

FieldInfo

PropertyInfo

EventInfo

MethodInfoConstructorInfo

ParameterInfo

Page 25: メタプログラミング C#

リフレクションの欠点

遅い

Page 26: メタプログラミング C#

メタプログラミング : Demo

• 簡単なメソッド (Add) の生成

static int Add(int x, int y){ return x + y;}

Page 27: メタプログラミング C#

メタプログラミング : DemoReflection.Emit による Add メソッドの動的生成

• 簡単なメソッド (Add) の生成• DynamicMethod

1. CIL (共通中間言語 ) を組み立てる

2. デリゲートを生成

Page 29: メタプログラミング C#

メタプログラミング : DemoReflection.Emit による Add メソッドの動的生成

var method = new DynamicMethod(“add”, …);

method.DefineParameter(1, ParameterAttributes.In, "x");

method.DefineParameter(2, ParameterAttributes.In, “y");

var generator = method.GetILGenerator();

generator.Emit(opcode: OpCodes.Ldarg_0);

generator.Emit(opcode: OpCodes.Ldarg_1);

generator.Emit(opcode: OpCodes.Add );

generator.Emit(opcode: OpCodes.Ret );

Func<int, int, int> newDelegate = method.CreateDelegate(…);

Page 30: メタプログラミング C#

メタプログラミング : Demo式木による Add メソッドの動的生成

• 簡単なメソッド (Add) の生成1. 式木を組み立てる

2. コンパイル +

x y

=>

(x, y)

Page 32: メタプログラミング C#

Visual Studio のデバッガーで add 式の構造を見る

Page 33: メタプログラミング C#

(x, y) => x + y の式木

Page 34: メタプログラミング C#

メタプログラミング : Demo式木による Add メソッドの動的生成

var x = Expression.Parameter(type: typeof(int));

var y = Expression.Parameter(type: typeof(int));

var add = Expression.Add (left: x, right: y);

var lambda = Expression.Lambda (add, x, y );

Func<int, int, int> newDelegate =(Func<int, int,

int>)lambda.Compile();

Page 35: メタプログラミング C#

メタプログラミング : DemoRoslyn による Add メソッドの動的生成

• 簡単なメソッド (Add) の生成1. スクリプトエンジンの作成

2. 名前空間のインポート

3. ソースコード ( 文字列 ) の実行

Page 36: メタプログラミング C#

Roslyn の入手先• Microsoft “Roslyn” CTP

• Visual Studio- NuGet

Page 37: メタプログラミング C#

メタプログラミング : DemoRoslyn による Add メソッドの動的生成

var engine = new ScriptEngine();

var session = engine.CreateSession();

session.ImportNamespace(…);

Func<int, int, int> newDelegate =session.Execute ("(Func<int, int, int>)((x, y) => x + y)");

Page 38: メタプログラミング C#

メタプログラミングによるプログラム生成の欠点

• ( リフレクションほどではないが)遅い• エラーが分かりにくい

Page 39: メタプログラミング C#

メタプログラミングの実行速度 : Demo

• 生成速度の比較1. Reflection.Emit による Add

2. 式木による Add

3. Roslyn による Add

• 実行速度の比較1. 静的な Add

2. Reflection.Emit による Add

3. 式木による Add

4. Roslyn による Add

Page 40: メタプログラミング C#

メタプログラミングの実行速度 : 結果

• 生成速度の比較

Page 41: メタプログラミング C#

メタプログラミングの実行速度 : 結果

• 実行速度の比較

Page 42: メタプログラミング C#

メタプログラミングの実行速度の改善

• 生成が遅い• 呼び出しの度に生成すると遅い

→ キャッシュで改善

Page 43: メタプログラミング C#

生成したデリゲートのキャッシュ

• デリゲートのキャッシュを用意Dictionary<string, Delegate> methods = new Dictionary<string, Delegate>();

1. キャッシュにない場合は、デリゲートを生成してキャッシュに入れる

2. キャッシュ内のデリゲートを呼ぶ

Page 44: メタプログラミング C#

メタプログラミングの実行速度の改善 : Demo• メソッド呼び出しの速度の比較

1. 静的なメソッド呼び出し

2. 動的なメソッド呼び出し1. リフレクション

2. dynamic

3. 動的にメソッドを生成して呼び出し - キャッシュ無し1. Reflection.Emit

2. 式木

3. Roslyn

4. 動的メソッドを生成して呼び出し - キャッシュ有り1. Reflection.Emit

2. 式木

3. Roslyn

Page 45: メタプログラミング C#

メタプログラミングの実行速度 : 結果

• 生成速度の比較

Page 46: メタプログラミング C#

メタプログラミングの実行速度 : 結果

• 実行速度の比較

Page 47: メタプログラミング C#

メタプログラミングの実行速度の改善 : 結果

Page 48: メタプログラミング C#

メタプログラミング応用例 : Demo

• デバッグ情報の出力• クラスやオブジェクト等を、型情報を使って文字列に変換

し、テストやデバッグで用いる。

• ASP.NET (Web アプリケーション /Web サービス ) のサーバー側の処理• クラスやオブジェクト等を、型情報を使って HTML に変

• XML シリアライザー• クラスやオブジェクト等を、型情報を使って XML に変換

Page 49: メタプログラミング C#

メタプログラミング - 応用編 : 続き

• オブジェクトの文字列変換を静的/動的に行う

• オブジェクトの文字列変換のメタプログラミング

• オブジェクトの文字列変換のメタプログラミング (Reflection.Emit 編)

• オブジェクトの文字列変換のメタプログラミング (式木編)

• オブジェクトの文字列変換のメタプログラミング (Roslyn 編)

• オブジェクトの文字列変換のメタプログラミング (パフォーマンスのテスト)

Page 50: メタプログラミング C#

まとめ

• C# ( や VB) でもメタプログラミングはできる• CodeDOM 、 Reflection.Emit 、式木、 Roslyn

•今後は Roslyn に期待

•Let’s enjoy metaprogramming!