SlideShare a Scribd company logo
Copyright 2020 @nuits_jp
C#メタプログラミング概略 in 2021
Atsushi Nakamura
Copyright 2020 @nuits_jp Slide 2
C# 9.0と合わせて、Source Generatorがリリースされました。※
それに伴い、メタプログラミングが活性化しているように見えます。
※厳密にはC#9.0世代のプラットフォームで実現しているが、C#9.0の
機能ではない?
https://docs.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-9
実際、C#7.3(netstandard2.0世代)などを明示的に指定しても利用できる
Background
Copyright 2020 @nuits_jp Slide 3
そこで今回は
Copyright 2020 @nuits_jp Slide 4
C#で利用可能な代表的なメタプログラミング手法について
• どういったものが存在し
• どういう時に何をつかえばいいのか?
その大枠を整理してみました。
Today's Goal
Copyright 2020 @nuits_jp Slide 5
Attention!
Copyright 2020 @nuits_jp Slide 6
本日の内容は、発表のために改めて調査・整理したものです。
私自身、普段あまり接点のない技術もありますので、抜け漏れ誤りがあ
るかもしれません。
また、多分に主観が含まれています。
お気づきの点があれば、ぜひ気軽に「やさしく」ご指摘ください。
Attention!
Copyright 2020 @nuits_jp
C# Metaprogramming Overview
About Me
Copyright 2020 @nuits_jp Slide 8
About Me
中村 充志 / Atsushi Nakamura
• リコージャパン株式会社 所属
• Enterprise(おもに金融)系SIerのITアーキテクト
• 「持続可能なソフトウェア」の探求がライフワーク
• 2021年の目標
1. いろいろハンズオン開催したい
xUnit、FluentAssertions、Moq、TDD
2. 構成管理フレンドリーなERモデリングツール作成
• Blog http://www.nuits.jp
https://zenn.dev/nuits_jp
• Twitter @nuits_jp
Copyright 2020 @nuits_jp
C# Metaprogramming Overview
What is metaprogramming?
Copyright 2020 @nuits_jp Slide 10
What is metaprogramming?
メタプログラミングとは何か?
Wikipediaより
“プログラミング技法の一種で、ロジックを直接コーディングす
るのではなく、あるパターンをもったロジックを生成する高位ロ
ジックによってプログラミングを行う方法、またその高位ロジッ
クを定義する方法のこと。”
https://ja.wikipedia.org/wiki/メタプログラミング
Copyright 2020 @nuits_jp Slide 11
• データベースのテーブル定義からクラスを生成する
• クラス構造を解析してJSONへシリアライズ/デシリアライズする
• シリアライズ/デシリアライズする際に、フィールドやプロパティに
属性を指定することで制御する
つまり
「メタデータをもとに、プログラムを生成するプログラミング」
具体例
Copyright 2020 @nuits_jp Slide 12
.NETのメタプログラミング技術(多分もっとある)
1. CodeDOM
2. Roslyn(ここではNuGet上のライブラリ利用のこと)
3. Reflection
4. Reflection.Emit
5. ExpressionTree
6. T4TextTemplates
7. Source Generator ← New!!
8. Fody / Mono.Cecil
メタプログラミング
Copyright 2020 @nuits_jp Slide 13
本日扱う.NETのメタプログラミング技術
1. CodeDOM
2. Roslyn(ここではNuGet上のライブラリ利用のこと)
3. Reflection
4. Reflection.Emit
5. ExpressionTree
6. T4TextTemplates
7. Source Generator ← New!!
8. Fody / Mono.Cecil
メタプログラミング
Copyright 2020 @nuits_jp Slide 14
本日扱う.NETのメタプログラミング技術
1. Reflection
2. ExpressionTree
3. T4TextTemplates
4. Source Generator
5. Fody / Mono.Cecil
メタプログラミング
Copyright 2020 @nuits_jp Slide 15
どういう時に、何を使えばいいの?
Copyright 2020 @nuits_jp Slide 16
主要なユーザーストーリーパターンで分類すると、つぎのラインでわけ
ることができる
1. Reflection
2. ExpressionTree
3. T4TextTemplates
4. Fody / Mono.Cecil
5. Source Generator
メタプログラミング
Copyright 2020 @nuits_jp Slide 17
主要なユーザーストーリーパターンで分類すると、つぎのラインでわけ
ることができる
1. Reflection
2. ExpressionTree
3. T4TextTemplates
4. Fody / Mono.Cecil
5. Source Generator
メタプログラミング
自分自身のためのメタプログラミング
Copyright 2020 @nuits_jp Slide 18
主要なユーザーストーリーパターンで分類すると、つぎのラインでわけ
ることができる
1. Reflection
2. ExpressionTree
3. T4TextTemplates
4. Fody / Mono.Cecil
5. Source Generator
メタプログラミング
自分自身のためのメタプログラミング
自分を含む誰かのためのメタプログラミング
Copyright 2020 @nuits_jp Slide 19
項目 内容
実現技術 • Reflection
• ExpressionTree
具体例 • Dapper
• JSON⇔オブジェクト変換ライブラリなど
利用タイミング 実行時
説明 サードパーティライブラリ(Dapper)などが、機能提供を実現するために、内部で
メタプログラミングを利用しているパターン。
SQLの実行結果をオブジェクトに詰めたり、その逆を実現するために、動的にメタ
モデルを解析し、メンバーの値を取得したり設定したりするのに利用する。
自分自身のためのメタプログラミング
Copyright 2020 @nuits_jp Slide 20
項目 内容
実現技術 • T4Template
• Source Generator
• Fody / Mono.Cecil
具体例 • PropertyChanged.Fody
• UnitGenerator
利用タイミング 実行前(詳細は個別に異なる)
説明 前述の例に限定されず、使用可能な用途が広い。誰かには自分も含まれる。
現時点では、第三者が利用する定型的な実装(ボイラーコード)の自動生成に利用
されていることが多い。
WPFなどにINotifyPropertyChanged実装を提供するPropertyChanged.Fody。
ビジネスロジックにバリューオブジェクトを扱うための定型的な実装を提供する
UnitGeneratorなどが存在する。
ただし今後は、Dapperなどが自分で利用するコードのためにSourceGeneratorを利
用すると言ったケースが増えてくるのではないかと思われる。早いので。
誰かのためのメタプログラミング
Copyright 2020 @nuits_jp
C# Metaprogramming Overview
Implementation example
Copyright 2020 @nuits_jp Slide 22
本日のお題
Equalsのオーバーライドをサポートするライブラリを作成する。
値の比較はIdentifier属性の宣言されたプロパティで行う。
制限事項
• Identifier属性は1クラス1プロパティにしかない前提とする
• 対象はint型のみサポート
• 非Genericなクラスにのみ対応すればよい(Generic、struct非対応)
• GetHashCodeの実装は割愛する
https://github.com/nuitsjp/MetaprogrammingOverviewIn2021
Copyright 2020 @nuits_jp Slide 23
以下のコードと同じ振る舞いを、各メタプログラミング技術で実装する。
実装例
public class Customer
{
public int Code { get; set; }
public override bool Equals(object other)
{
if (other is Customer customer)
{
return Code.Equals(customer.Code);
}
return false;
}
}
Copyright 2020 @nuits_jp Slide 24
Reflection
Copyright 2020 @nuits_jp Slide 25
1. 圧倒的に簡単
2. 圧倒的に遅い
上記のような特徴があるため、速度がそれほど重要にならない、一度だ
け実行できればよい、何らかの処理を自動化するためのツールなどで使
い勝手が良い。
Reflectionの特徴
Copyright 2020 @nuits_jp Slide 26
No. 実装 実行時間
1 Not metaprogramming 2.798 ns
2 Reflection 2,891.742 ns
3 Reflection with Cache
4 ExpressionTree
5 ExpressionTree with Cache
6 T4Template
7 Source Generator
8 Fody
ざっと1,000倍!
(とは言え0.002ミリ秒)
Copyright 2020 @nuits_jp Slide 27
Reflection with Cache
Copyright 2020 @nuits_jp Slide 28
No. 実装 実行時間
1 Not metaprogramming 2.798 ns
2 Reflection 2,891.742 ns
3 Reflection with Cache 214.812 ns
4 ExpressionTree
5 ExpressionTree with Cache
6 T4Template
7 Source Generator
8 Fody
Reflection with Cache
Copyright 2020 @nuits_jp Slide 29
• 全体の処理の90%を占めるコードを回避できる
• 繰り返し実行すればするほど効果が高い
• StaticType Cachingパターンがハマりやすい
Reflection with Cache
Copyright 2020 @nuits_jp Slide 30
ExpressionTree
Copyright 2020 @nuits_jp Slide 31
No. 実装 実行時間
1 Not metaprogramming 2.798 ns
2 Reflection 2,891.742 ns
3 Reflection with Cache 214.812 ns
4 ExpressionTree 85,835.928 ns
5 ExpressionTree with Cache
6 T4Template
7 Source Generator
8 Fody
ExpressionTree
Compileが非常に遅い
Copyright 2020 @nuits_jp Slide 32
ExpressionTree with Cache
Copyright 2020 @nuits_jp Slide 33
No. 実装 実行時間
1 Not metaprogramming 2.798 ns
2 Reflection 2,891.742 ns
3 Reflection with Cache 214.812 ns
4 ExpressionTree 85,835.928 ns
5 ExpressionTree with Cache 7.914 ns
6 T4Template
7 Source Generator
8 Fody
ExpressionTree with Cache
Copyright 2020 @nuits_jp Slide 34
• Notメタプログラミングと、おおむね同等といってよい
(nsの世界なので)
• ExpressionTreeはキャッシングとセット
• キャッシングしないならReflectionでいい
• このため型変換系(ORMや通信)のライブラリで使い勝手が良い
• やはりStaticType Cachingパターンがハマりやすい
ExpressionTree with Cache
Copyright 2020 @nuits_jp Slide 35
T4Template
Copyright 2020 @nuits_jp Slide 36
No. 実装 実行時間
1 Not metaprogramming 2.798 ns
2 Reflection 2,891.742 ns
3 Reflection with Cache 214.812 ns
4 ExpressionTree 85,835.928 ns
5 ExpressionTree with Cache 7.914 ns
6 T4Template 2.251 ns
7 Source Generator
8 Fody
T4Template
Copyright 2020 @nuits_jp Slide 37
• 速度は非メタプログラミングと完全に同等(当たり前)
• ちょっと癖がある
• 本来生成コードは構成管理したくない
• レビューやテスト(の特にカバレージチェック)に悪影響
• コード生成する対象コードが増えても、自動生成されない点に特
に注意(削除時は生成コードを一度削除しないとダメ?)
• ライブラリとして再配布する場合「利用者が」理解して使う必要有
• IDE依存でCI/CD時に生成は大変(VS、VS for Max、Riderが対応)
• でも複雑なコード生成も、すごく簡単(多分一番簡単)
T4Template
Copyright 2020 @nuits_jp Slide 38
Source Generator
Copyright 2020 @nuits_jp Slide 39
No. 実装 実行時間
1 Not metaprogramming 2.798 ns
2 Reflection 2,891.742 ns
3 Reflection with Cache 214.812 ns
4 ExpressionTree 85,835.928 ns
5 ExpressionTree with Cache 7.914 ns
6 T4Template 2.251 ns
7 Source Generator 2.259 ns
8 Fody
Source Generator
Copyright 2020 @nuits_jp Slide 40
• やはり速度は非メタプログラミングと完全に同等(当たり前)
• ソースはメモリ展開されてコンパイルされる
• でもデバッグはできちゃう
• そのため利用者に「理解を求める」必要がなく再配布しやすい
• 上記の理由から、今後例えばDapperやJSON変換のようなライブラリ
などで、型変換コードがSource Generatorへ移行していく可能性があ
る?しらんけど・・・
• Analyzerとの相性も良く、利用者に使いやすいものを作りやすい
• 実は生成コードのテンプレートにT4がすごく使いやすい
• IDE非依存なので、CI/CDなんかでも使いやすい
Source Generator
Copyright 2020 @nuits_jp Slide 41
Fody
Copyright 2020 @nuits_jp Slide 42
No. 実装 実行時間
1 Not metaprogramming 2.798 ns
2 Reflection 2,891.742 ns
3 Reflection with Cache 214.812 ns
4 ExpressionTree 85,835.928 ns
5 ExpressionTree with Cache 7.914 ns
6 T4Template 2.251 ns
7 Source Generator 2.259 ns
8 Fody 2.584 ns
Fody
Copyright 2020 @nuits_jp Slide 43
• 他と比較して、既存コードの改変が行えるのが最大の特徴
• AOP的な実装をするのにコード改変が必要なことがある
• 例)既存メソッドにトラッキングコードを埋め込む
• 例)例外時のリトライ処理を定型的に埋め込む
• 逆にいうと、ほかの手段で可能なことはやらない方がいい
• 学習コストが高い(類似性のない新言語を1つ覚えるに等しい)
• 単純に生産性が非常に低い
• 1例としてSource Generator比の3倍のソースコード
• 品質の確保も困難
• 1例としてSource Generatorの16倍のテストケース
Fody
Copyright 2020 @nuits_jp
C# Metaprogramming Overview
まとめ
Copyright 2020 @nuits_jp Slide 45
IComparableのボイラーコードを生成するライブラリを作ってるので、
完成したらよかったら使ってね!
https://github.com/nuitsjp/ComparableGenerator
まとめ
Copyright 2020 @nuits_jp Slide 46
じゃなかった
Copyright 2020 @nuits_jp Slide 47
.NETのメタプログラミング技術(多分もっとある)
1. CodeDOM
2. Roslyn(ここではNuGet上のライブラリ利用のこと)
3. Reflection
4. Reflection.Emit
5. ExpressionTree
6. T4TextTemplates
7. Source Generator ← New!!
8. Fody / Mono.Cecil
メタプログラミング
Copyright 2020 @nuits_jp Slide 48
• それぞれ適材適所
• いつものとおり、銀の弾丸はない
• 向いているものを、向いている場所で使いましょう
• 特に!IL弄るのは最終手段
注意点
Copyright 2020 @nuits_jp Slide 49
ThankYou!
おしまい!

More Related Content

PDF
ゲーム開発者のための C++11/C++14
PPTX
C#とILとネイティブと
PDF
MagicOnion入門
PDF
ドメイン駆動設計 ( DDD ) をやってみよう
PDF
【Unite Tokyo 2019】Understanding C# Struct All Things
PDF
Riderはいいぞ!
PDF
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
PPTX
async/await のしくみ
ゲーム開発者のための C++11/C++14
C#とILとネイティブと
MagicOnion入門
ドメイン駆動設計 ( DDD ) をやってみよう
【Unite Tokyo 2019】Understanding C# Struct All Things
Riderはいいぞ!
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
async/await のしくみ

What's hot (20)

PDF
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
ODP
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
PDF
規格書で読むC++11のスレッド
PDF
今日からできる!簡単 .NET 高速化 Tips
PDF
できる!並列・並行プログラミング
PDF
Tackling Complexity
PDF
[CEDEC 2021] 運用中タイトルでも怖くない! 『メルクストーリア』におけるハイパフォーマンス・ローコストなリアルタイム通信技術の導入事例
PPTX
世界一わかりやすいClean Architecture
PDF
IL2CPPに関する軽い話
PPTX
SPAセキュリティ入門~PHP Conference Japan 2021
PDF
Spring Boot の Web アプリケーションを Docker に載せて AWS ECS で動かしている話
PDF
Assembly Definition あれやこれ
PDF
それはYAGNIか? それとも思考停止か?
PDF
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
PPTX
Python / BlueprintによるUnreal Engineの自動化 / GTMF2019
PPTX
C#や.NET Frameworkがやっていること
PDF
Constexprとtemplateでコンパイル時にfizz buzz
PDF
ソーシャルゲームのためのデータベース設計
PPTX
プログラマが欲しい仕様書とは
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
規格書で読むC++11のスレッド
今日からできる!簡単 .NET 高速化 Tips
できる!並列・並行プログラミング
Tackling Complexity
[CEDEC 2021] 運用中タイトルでも怖くない! 『メルクストーリア』におけるハイパフォーマンス・ローコストなリアルタイム通信技術の導入事例
世界一わかりやすいClean Architecture
IL2CPPに関する軽い話
SPAセキュリティ入門~PHP Conference Japan 2021
Spring Boot の Web アプリケーションを Docker に載せて AWS ECS で動かしている話
Assembly Definition あれやこれ
それはYAGNIか? それとも思考停止か?
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
Python / BlueprintによるUnreal Engineの自動化 / GTMF2019
C#や.NET Frameworkがやっていること
Constexprとtemplateでコンパイル時にfizz buzz
ソーシャルゲームのためのデータベース設計
プログラマが欲しい仕様書とは
Ad

Similar to C#メタプログラミング概略 in 2021 (20)

PDF
C#でもメタプログラミングがしたい!!
PPTX
メタな感じのプログラミング(プロ生 + わんくま 071118)
PPTX
Eclipse modeling projectの概要
PDF
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
PDF
Object-Functional Analysis and Design : 次世代モデリングパラダイムへの道標
PPT
メタプログラミングって何だろう
PDF
2010 icse-an analysis of the variability in forty preprocessor-based software...
PDF
Object-Funcational Analysis and design
PPTX
GoF デザインパターン 2009
PDF
C#勉強会 ~ C#9の新機能 ~
PDF
JavaWorld Day 2009 Scala
PDF
C++ Template Meta Programming の紹介@社内勉強会
PDF
テンプレートメタプログラミング as 式
PDF
Clojure
PDF
C# から java へのプログラム移植で体験したtddの効果は?
PDF
関数モデル 【クラウドアプリケーションのためのオブジェクト指向分析設計講座 第8回】
PDF
Scalable Generator: Using Scala in SIer Business (ScalaMatsuri)
PDF
C++ Template Metaprogramming
PPTX
テストの視点からのモデリング(公開用) #wacate
C#でもメタプログラミングがしたい!!
メタな感じのプログラミング(プロ生 + わんくま 071118)
Eclipse modeling projectの概要
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
Object-Functional Analysis and Design : 次世代モデリングパラダイムへの道標
メタプログラミングって何だろう
2010 icse-an analysis of the variability in forty preprocessor-based software...
Object-Funcational Analysis and design
GoF デザインパターン 2009
C#勉強会 ~ C#9の新機能 ~
JavaWorld Day 2009 Scala
C++ Template Meta Programming の紹介@社内勉強会
テンプレートメタプログラミング as 式
Clojure
C# から java へのプログラム移植で体験したtddの効果は?
関数モデル 【クラウドアプリケーションのためのオブジェクト指向分析設計講座 第8回】
Scalable Generator: Using Scala in SIer Business (ScalaMatsuri)
C++ Template Metaprogramming
テストの視点からのモデリング(公開用) #wacate
Ad

More from Atsushi Nakamura (19)

PPSX
Settings SyncとCodespaceで体験する新世代へのパラダイムシフト
PPTX
Unicodeで半角全角を扱うAmbiguous(曖昧さ)とUncertainty(不確実性)の恐怖
PPTX
世界一わかりやすいClean Architecture - DroidKaigiバージョン
PPTX
世界一わかりやすいClean Architecture release-preview
PPTX
世界一わかりやすいClean Architecture alpha-1
PPTX
Visual Studio 2019で始める「WPF on .NET Core 3.0」開発
PPTX
Desktop app dev strategy for .net core 3.0
PPTX
継続的にテスト可能な設計を考える
PPTX
継続的にテスト可能な設計を考える ベータ版
PPTX
α版 継続的にテスト可能な設計を考える
PDF
App center analyticsを使い倒そう
PPTX
Old:App center analyticsを使い倒そう
PPTX
Xamarin.forms navigation overview
PPTX
App center analyticsを使い倒そう
PPTX
「関心の分離」と「疎結合」 ソフトウェアアーキテクチャのひとかけら
PPTX
Blue monkey architecture overview
PPTX
Xamarin Dev days 2 xamarin.forms ja
PPTX
Why prism for xamarin.forms
PPTX
Enterpriseから見たXamarinの可能性
Settings SyncとCodespaceで体験する新世代へのパラダイムシフト
Unicodeで半角全角を扱うAmbiguous(曖昧さ)とUncertainty(不確実性)の恐怖
世界一わかりやすいClean Architecture - DroidKaigiバージョン
世界一わかりやすいClean Architecture release-preview
世界一わかりやすいClean Architecture alpha-1
Visual Studio 2019で始める「WPF on .NET Core 3.0」開発
Desktop app dev strategy for .net core 3.0
継続的にテスト可能な設計を考える
継続的にテスト可能な設計を考える ベータ版
α版 継続的にテスト可能な設計を考える
App center analyticsを使い倒そう
Old:App center analyticsを使い倒そう
Xamarin.forms navigation overview
App center analyticsを使い倒そう
「関心の分離」と「疎結合」 ソフトウェアアーキテクチャのひとかけら
Blue monkey architecture overview
Xamarin Dev days 2 xamarin.forms ja
Why prism for xamarin.forms
Enterpriseから見たXamarinの可能性

C#メタプログラミング概略 in 2021

  • 2. Copyright 2020 @nuits_jp Slide 2 C# 9.0と合わせて、Source Generatorがリリースされました。※ それに伴い、メタプログラミングが活性化しているように見えます。 ※厳密にはC#9.0世代のプラットフォームで実現しているが、C#9.0の 機能ではない? https://docs.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-9 実際、C#7.3(netstandard2.0世代)などを明示的に指定しても利用できる Background
  • 3. Copyright 2020 @nuits_jp Slide 3 そこで今回は
  • 4. Copyright 2020 @nuits_jp Slide 4 C#で利用可能な代表的なメタプログラミング手法について • どういったものが存在し • どういう時に何をつかえばいいのか? その大枠を整理してみました。 Today's Goal
  • 5. Copyright 2020 @nuits_jp Slide 5 Attention!
  • 6. Copyright 2020 @nuits_jp Slide 6 本日の内容は、発表のために改めて調査・整理したものです。 私自身、普段あまり接点のない技術もありますので、抜け漏れ誤りがあ るかもしれません。 また、多分に主観が含まれています。 お気づきの点があれば、ぜひ気軽に「やさしく」ご指摘ください。 Attention!
  • 7. Copyright 2020 @nuits_jp C# Metaprogramming Overview About Me
  • 8. Copyright 2020 @nuits_jp Slide 8 About Me 中村 充志 / Atsushi Nakamura • リコージャパン株式会社 所属 • Enterprise(おもに金融)系SIerのITアーキテクト • 「持続可能なソフトウェア」の探求がライフワーク • 2021年の目標 1. いろいろハンズオン開催したい xUnit、FluentAssertions、Moq、TDD 2. 構成管理フレンドリーなERモデリングツール作成 • Blog http://www.nuits.jp https://zenn.dev/nuits_jp • Twitter @nuits_jp
  • 9. Copyright 2020 @nuits_jp C# Metaprogramming Overview What is metaprogramming?
  • 10. Copyright 2020 @nuits_jp Slide 10 What is metaprogramming? メタプログラミングとは何か? Wikipediaより “プログラミング技法の一種で、ロジックを直接コーディングす るのではなく、あるパターンをもったロジックを生成する高位ロ ジックによってプログラミングを行う方法、またその高位ロジッ クを定義する方法のこと。” https://ja.wikipedia.org/wiki/メタプログラミング
  • 11. Copyright 2020 @nuits_jp Slide 11 • データベースのテーブル定義からクラスを生成する • クラス構造を解析してJSONへシリアライズ/デシリアライズする • シリアライズ/デシリアライズする際に、フィールドやプロパティに 属性を指定することで制御する つまり 「メタデータをもとに、プログラムを生成するプログラミング」 具体例
  • 12. Copyright 2020 @nuits_jp Slide 12 .NETのメタプログラミング技術(多分もっとある) 1. CodeDOM 2. Roslyn(ここではNuGet上のライブラリ利用のこと) 3. Reflection 4. Reflection.Emit 5. ExpressionTree 6. T4TextTemplates 7. Source Generator ← New!! 8. Fody / Mono.Cecil メタプログラミング
  • 13. Copyright 2020 @nuits_jp Slide 13 本日扱う.NETのメタプログラミング技術 1. CodeDOM 2. Roslyn(ここではNuGet上のライブラリ利用のこと) 3. Reflection 4. Reflection.Emit 5. ExpressionTree 6. T4TextTemplates 7. Source Generator ← New!! 8. Fody / Mono.Cecil メタプログラミング
  • 14. Copyright 2020 @nuits_jp Slide 14 本日扱う.NETのメタプログラミング技術 1. Reflection 2. ExpressionTree 3. T4TextTemplates 4. Source Generator 5. Fody / Mono.Cecil メタプログラミング
  • 15. Copyright 2020 @nuits_jp Slide 15 どういう時に、何を使えばいいの?
  • 16. Copyright 2020 @nuits_jp Slide 16 主要なユーザーストーリーパターンで分類すると、つぎのラインでわけ ることができる 1. Reflection 2. ExpressionTree 3. T4TextTemplates 4. Fody / Mono.Cecil 5. Source Generator メタプログラミング
  • 17. Copyright 2020 @nuits_jp Slide 17 主要なユーザーストーリーパターンで分類すると、つぎのラインでわけ ることができる 1. Reflection 2. ExpressionTree 3. T4TextTemplates 4. Fody / Mono.Cecil 5. Source Generator メタプログラミング 自分自身のためのメタプログラミング
  • 18. Copyright 2020 @nuits_jp Slide 18 主要なユーザーストーリーパターンで分類すると、つぎのラインでわけ ることができる 1. Reflection 2. ExpressionTree 3. T4TextTemplates 4. Fody / Mono.Cecil 5. Source Generator メタプログラミング 自分自身のためのメタプログラミング 自分を含む誰かのためのメタプログラミング
  • 19. Copyright 2020 @nuits_jp Slide 19 項目 内容 実現技術 • Reflection • ExpressionTree 具体例 • Dapper • JSON⇔オブジェクト変換ライブラリなど 利用タイミング 実行時 説明 サードパーティライブラリ(Dapper)などが、機能提供を実現するために、内部で メタプログラミングを利用しているパターン。 SQLの実行結果をオブジェクトに詰めたり、その逆を実現するために、動的にメタ モデルを解析し、メンバーの値を取得したり設定したりするのに利用する。 自分自身のためのメタプログラミング
  • 20. Copyright 2020 @nuits_jp Slide 20 項目 内容 実現技術 • T4Template • Source Generator • Fody / Mono.Cecil 具体例 • PropertyChanged.Fody • UnitGenerator 利用タイミング 実行前(詳細は個別に異なる) 説明 前述の例に限定されず、使用可能な用途が広い。誰かには自分も含まれる。 現時点では、第三者が利用する定型的な実装(ボイラーコード)の自動生成に利用 されていることが多い。 WPFなどにINotifyPropertyChanged実装を提供するPropertyChanged.Fody。 ビジネスロジックにバリューオブジェクトを扱うための定型的な実装を提供する UnitGeneratorなどが存在する。 ただし今後は、Dapperなどが自分で利用するコードのためにSourceGeneratorを利 用すると言ったケースが増えてくるのではないかと思われる。早いので。 誰かのためのメタプログラミング
  • 21. Copyright 2020 @nuits_jp C# Metaprogramming Overview Implementation example
  • 22. Copyright 2020 @nuits_jp Slide 22 本日のお題 Equalsのオーバーライドをサポートするライブラリを作成する。 値の比較はIdentifier属性の宣言されたプロパティで行う。 制限事項 • Identifier属性は1クラス1プロパティにしかない前提とする • 対象はint型のみサポート • 非Genericなクラスにのみ対応すればよい(Generic、struct非対応) • GetHashCodeの実装は割愛する https://github.com/nuitsjp/MetaprogrammingOverviewIn2021
  • 23. Copyright 2020 @nuits_jp Slide 23 以下のコードと同じ振る舞いを、各メタプログラミング技術で実装する。 実装例 public class Customer { public int Code { get; set; } public override bool Equals(object other) { if (other is Customer customer) { return Code.Equals(customer.Code); } return false; } }
  • 24. Copyright 2020 @nuits_jp Slide 24 Reflection
  • 25. Copyright 2020 @nuits_jp Slide 25 1. 圧倒的に簡単 2. 圧倒的に遅い 上記のような特徴があるため、速度がそれほど重要にならない、一度だ け実行できればよい、何らかの処理を自動化するためのツールなどで使 い勝手が良い。 Reflectionの特徴
  • 26. Copyright 2020 @nuits_jp Slide 26 No. 実装 実行時間 1 Not metaprogramming 2.798 ns 2 Reflection 2,891.742 ns 3 Reflection with Cache 4 ExpressionTree 5 ExpressionTree with Cache 6 T4Template 7 Source Generator 8 Fody ざっと1,000倍! (とは言え0.002ミリ秒)
  • 27. Copyright 2020 @nuits_jp Slide 27 Reflection with Cache
  • 28. Copyright 2020 @nuits_jp Slide 28 No. 実装 実行時間 1 Not metaprogramming 2.798 ns 2 Reflection 2,891.742 ns 3 Reflection with Cache 214.812 ns 4 ExpressionTree 5 ExpressionTree with Cache 6 T4Template 7 Source Generator 8 Fody Reflection with Cache
  • 29. Copyright 2020 @nuits_jp Slide 29 • 全体の処理の90%を占めるコードを回避できる • 繰り返し実行すればするほど効果が高い • StaticType Cachingパターンがハマりやすい Reflection with Cache
  • 30. Copyright 2020 @nuits_jp Slide 30 ExpressionTree
  • 31. Copyright 2020 @nuits_jp Slide 31 No. 実装 実行時間 1 Not metaprogramming 2.798 ns 2 Reflection 2,891.742 ns 3 Reflection with Cache 214.812 ns 4 ExpressionTree 85,835.928 ns 5 ExpressionTree with Cache 6 T4Template 7 Source Generator 8 Fody ExpressionTree Compileが非常に遅い
  • 32. Copyright 2020 @nuits_jp Slide 32 ExpressionTree with Cache
  • 33. Copyright 2020 @nuits_jp Slide 33 No. 実装 実行時間 1 Not metaprogramming 2.798 ns 2 Reflection 2,891.742 ns 3 Reflection with Cache 214.812 ns 4 ExpressionTree 85,835.928 ns 5 ExpressionTree with Cache 7.914 ns 6 T4Template 7 Source Generator 8 Fody ExpressionTree with Cache
  • 34. Copyright 2020 @nuits_jp Slide 34 • Notメタプログラミングと、おおむね同等といってよい (nsの世界なので) • ExpressionTreeはキャッシングとセット • キャッシングしないならReflectionでいい • このため型変換系(ORMや通信)のライブラリで使い勝手が良い • やはりStaticType Cachingパターンがハマりやすい ExpressionTree with Cache
  • 35. Copyright 2020 @nuits_jp Slide 35 T4Template
  • 36. Copyright 2020 @nuits_jp Slide 36 No. 実装 実行時間 1 Not metaprogramming 2.798 ns 2 Reflection 2,891.742 ns 3 Reflection with Cache 214.812 ns 4 ExpressionTree 85,835.928 ns 5 ExpressionTree with Cache 7.914 ns 6 T4Template 2.251 ns 7 Source Generator 8 Fody T4Template
  • 37. Copyright 2020 @nuits_jp Slide 37 • 速度は非メタプログラミングと完全に同等(当たり前) • ちょっと癖がある • 本来生成コードは構成管理したくない • レビューやテスト(の特にカバレージチェック)に悪影響 • コード生成する対象コードが増えても、自動生成されない点に特 に注意(削除時は生成コードを一度削除しないとダメ?) • ライブラリとして再配布する場合「利用者が」理解して使う必要有 • IDE依存でCI/CD時に生成は大変(VS、VS for Max、Riderが対応) • でも複雑なコード生成も、すごく簡単(多分一番簡単) T4Template
  • 38. Copyright 2020 @nuits_jp Slide 38 Source Generator
  • 39. Copyright 2020 @nuits_jp Slide 39 No. 実装 実行時間 1 Not metaprogramming 2.798 ns 2 Reflection 2,891.742 ns 3 Reflection with Cache 214.812 ns 4 ExpressionTree 85,835.928 ns 5 ExpressionTree with Cache 7.914 ns 6 T4Template 2.251 ns 7 Source Generator 2.259 ns 8 Fody Source Generator
  • 40. Copyright 2020 @nuits_jp Slide 40 • やはり速度は非メタプログラミングと完全に同等(当たり前) • ソースはメモリ展開されてコンパイルされる • でもデバッグはできちゃう • そのため利用者に「理解を求める」必要がなく再配布しやすい • 上記の理由から、今後例えばDapperやJSON変換のようなライブラリ などで、型変換コードがSource Generatorへ移行していく可能性があ る?しらんけど・・・ • Analyzerとの相性も良く、利用者に使いやすいものを作りやすい • 実は生成コードのテンプレートにT4がすごく使いやすい • IDE非依存なので、CI/CDなんかでも使いやすい Source Generator
  • 41. Copyright 2020 @nuits_jp Slide 41 Fody
  • 42. Copyright 2020 @nuits_jp Slide 42 No. 実装 実行時間 1 Not metaprogramming 2.798 ns 2 Reflection 2,891.742 ns 3 Reflection with Cache 214.812 ns 4 ExpressionTree 85,835.928 ns 5 ExpressionTree with Cache 7.914 ns 6 T4Template 2.251 ns 7 Source Generator 2.259 ns 8 Fody 2.584 ns Fody
  • 43. Copyright 2020 @nuits_jp Slide 43 • 他と比較して、既存コードの改変が行えるのが最大の特徴 • AOP的な実装をするのにコード改変が必要なことがある • 例)既存メソッドにトラッキングコードを埋め込む • 例)例外時のリトライ処理を定型的に埋め込む • 逆にいうと、ほかの手段で可能なことはやらない方がいい • 学習コストが高い(類似性のない新言語を1つ覚えるに等しい) • 単純に生産性が非常に低い • 1例としてSource Generator比の3倍のソースコード • 品質の確保も困難 • 1例としてSource Generatorの16倍のテストケース Fody
  • 44. Copyright 2020 @nuits_jp C# Metaprogramming Overview まとめ
  • 45. Copyright 2020 @nuits_jp Slide 45 IComparableのボイラーコードを生成するライブラリを作ってるので、 完成したらよかったら使ってね! https://github.com/nuitsjp/ComparableGenerator まとめ
  • 46. Copyright 2020 @nuits_jp Slide 46 じゃなかった
  • 47. Copyright 2020 @nuits_jp Slide 47 .NETのメタプログラミング技術(多分もっとある) 1. CodeDOM 2. Roslyn(ここではNuGet上のライブラリ利用のこと) 3. Reflection 4. Reflection.Emit 5. ExpressionTree 6. T4TextTemplates 7. Source Generator ← New!! 8. Fody / Mono.Cecil メタプログラミング
  • 48. Copyright 2020 @nuits_jp Slide 48 • それぞれ適材適所 • いつものとおり、銀の弾丸はない • 向いているものを、向いている場所で使いましょう • 特に!IL弄るのは最終手段 注意点
  • 49. Copyright 2020 @nuits_jp Slide 49 ThankYou! おしまい!

Editor's Notes

  • #2: それでは始めます。 よろしくお願いします。
  • #3: さてC#9.0のリリースとあわせて、Source Generatorがリリースされましたね。 昨今、一部でメタプログラミングが活性化しているように、個人的には感じています。
  • #4: そこで今回は
  • #5: C#で利用可能な代表的なメタプログラミングについて どういったものが存在し どういうときに、何を使えばいいのか? その大枠を整理してみました。
  • #6: 1点ご注意いただきたいことがあります。
  • #7: 今回の発表内容について 抜け漏れ誤りがあるかもしれませんし 多分に主観が含まれています。 お気づきの点がありましたら、ぜひ気軽にかつ「やさしく」ご指摘ください。
  • #8: では本題の前に、自己紹介を。
  • #9: 中村充志と申します。 リコージャパンという会社に所属して 金融系のエンプラ界隈でアーキテクトをやっています。 ブログやTwitterやってますので、フォローしてもらえると喜びます。
  • #10: では本題へ。 まず、はじめに メタプログラミングとはなにか?おさらいしましょう。
  • #11: Wikipedia様によると
  • #12: たとえば、データベース構造からクラスを生成したり クラスを構造解析してJSONへシリアライズしたり つまり、メタデータをもとに、プログラムを生成するプログラミングのことをメタプログラミングと呼びます。
  • #13: .NETの代表的なメタプログラミングを集めてみました。 皆さんの推しが入っていなかったらゴメンなさい。 Source Generatorが追加されたのがトピックですね。 今回はこれらのうち
  • #14: この辺は除外させていただいて
  • #15: この5つについてお話します。
  • #16: まずは、どういうときに、何をつかえばいいのか? から、お話しましょう。
  • #17: その際、このラインで区切って考えると分かりやすいです。
  • #18: 上は、基本的に自分自身のためにメタプログラミングする技法で
  • #19: それ以外は、自分を含む、誰かのために利用します。 うん、わからん。
  • #20: 前者について 例えばDapperなどが 実行時に SQLの実行結果をオブジェクトにつめたり、その逆を実現するために 動的に構造解析して、メンバを取得したり設定したりするのに 利用される技術です。
  • #21: 後者は、前者のように限定されず、使用用途が非常に広いです。 現時点では、定型的実装つまりボイラーコードの自動生成に 利用されているのをよく見かけます。 たとえば、WPF向けにINotifyPropertyChangedの実装を自動的に生成したり DDDするためのバリューオブジェクトの典型的な実装を提供したりです。 実行前に解決されるため、動作が非常に軽快です。 これまで、これ系のメタプログラミングは、ちょっと癖が強かったのですが Source Generatorはその辺りがだいぶ解消されています。 そのため、今後は、Dapperみたいなライブラリが、自分で利用するコードのために Source Generatorを利用するといったケースも増えてくるかもしれません。
  • #22: ということで、実際のサンプルを見ていきましょう。
  • #23: 今回はEqualsのオーバーライドをサポートするライブラリをお題とします。 Identifier属性を宣言されたプロパティで同値比較します。 これをまじめに作ると、結構なボリュームになってしまうので 記載しているような制約を前提とします。 今回、時間の兼ね合いで、コードはざっとだけお見せします。 詳細はGithubに公開しているので、良かったらご覧ください。
  • #24: こんな参照じゃなくて、内部のメンバーで比較するコードを いろんな手法で生成します。
  • #25: てことで、まずはReflectionからいきましょう! ソリューションを開くと、こんな感じで実装技術ごとにフォルダに分けられて整理されています。 一番上は、Notメタなコードです。 Reflectionは二番目ですね。 開くと、3つプロジェクトがあります。 だいたい、どの実装技術も同じ構成です。 まず実装技術名のプロジェクトがあって、こちらに構文解析される側のコードが入っています。 CustomerとEmployeeがありますね。 ReflectionではこれらでEqualsをオーバーライドして、内部からEqualsクラスのInvokeに 処理を委譲しています。 この実装は、メタプログラミングとついているプロジェクトに含まれています。 中をみると、TypeからIdentifier属性が付いているプロパティインフォを取得しているのが見て取れます。 取得したプロパティインフォを使って、値比較するプロパティの値を取得しています。 PPTXへ
  • #26: さてご覧いただいたように、Reflectionは実装が簡単です。 半面、動作速度が遅いです。 そのため、一度だけ実行すればよい自動化ツールの作成なんかで使い勝手が良いです ちなみにどれくらい遅いかというと、今回のコードで
  • #27: ざっと1000倍くらい遅いです。 とは言え、単位なナノセコンドなので、使えない遅さという話ではないです。
  • #28: ただ、これは改善の余地があって 構造解析のコードをキャッシングすることで性能を改善できます。 実際にコードを見てみましょう。 VSへ Customerコードは同じです。 EqualsのInvokeの実装が異なります。 内部でキャッシュされたInstanceオブジェクトのInvokeInnerメソッドを呼び出しています。 その中では、先ほどのプロパティインフォを取得するコードが含まれていませんね。 こんな感じでキャッシュを使いまわします。 詳細は気になる人は、Githubをご覧ください。 で、実行速度はというと PPTXへ
  • #29: だいたい10分の1くらいになりました。
  • #30: これは、繰り返し実行すればするほど、差が大きくなります。 Genericな実装でキャッシングを実行する場合、 Static Type Cachingパターンが非常にはまりやすいので ぜひ活用を検討してください。
  • #31: 続いてExpression Treeです。 実際に動的メタプログラミングではこれが利用されているケースが非常に多いです。 VSへ CustomerコードはReflectionと全く同一です。 実装を見てみるとなんだか少し違いますね。 Expressionというのを組み立てて、最後にCompileしてラムダを生成しています。 このラムダを値の取得に利用します。 さて、この実行速度なのですが PPTXへ
  • #32: めっちゃ遅いです! これは実行の都度、Compileが走っているからです。
  • #33: ということでReflectionと同じようにキャッシングすると
  • #34: 劇的に早くすることができます。
  • #35: ナノセコンドの世界ですので、ノットメタな世界とおおむね同等といっていいと思います。 Expression Treeは基本的にキャッシングとセットで使いましょう。 こういった特徴があるので、型変換を伴うORMや通信系のライブラリと相性が非常によいです。
  • #36: 続いてT4テンプレートです。 VSへ T4だけ、メタプログラミングとつくプロジェクトがありません。 これはT4はCustomerのコードと同じプロジェクト内に.ttファイルを配置する必要があるからです。 中身を見てみましょう。 ASP.NETのRazerにもにた、普通のテンプレート構文なファイルですね。 詳細は割愛しますが、このDTEというのが、Visual Studioを表すオブジェクトで そこから含まれるコードのメタ情報を取得します。 この下に、Equalsのコードが生成されます。 これを実行すると PPTXへ
  • #37: 当たり前ですが、非常に速いです。
  • #38: 非メタな実装と完全に同等です。 ただ、いろいろ癖がありまして・・・ 本来自動生成するコードは構成管理したくないですよね。 PRにのってきちゃうと、レビューが大変ですし、テストカバレージの扱いにも困ります。 また、コード生成する対象コードが増減しても、自動的に追従してくれないって問題もあります。 VSへ たとえば、Itemクラス、コメントアウトされていなものを外してコンパイルしても・・・ 生成されません。 T4ファイルを保存するか、右クリから実行すると作成されます。 削除するときは右クリ実行しても、こんな中途半端な形で生成されるので 一度全部消してから再生成します。 PPTX 特に最後の問題は、再配布して利用する場合に、利用者がその特徴を理解して使わないといけなかったりするのが困りごとです。 またIDE依存なのでCI/CD時に生成ということができません。 でも、複雑なコード生成も、ものすごく簡単にできちゃうというメリットがあります。
  • #39: つぎはいよいよソースジェネレーターです Customerクラスはこれまで同様です。 メタプロ側のプロジェクトの中のSourceGenerator.csでソース生成してます。 見ると分かりますが、べたにStringBuilderにAppend、Appendしてます。 ストリングインターポレーションだとパフォーマンスに問題があるので、そこそこ大きなコード生成はこうする必要があります。 でも、これ面倒ですよね? ってことで、T4テンプレートの出番です。 こいつです。 さっきはプロパティのカスタムツールが、ほにゃららGeneratorになっていましたが 今度はほにゃららProcessorになってます。 こうすると、Append、Appendするコードをテンプレートから生成してくれます。 お前は神か!って感じですよね。 こいつは、配布されないので利用者が意識しなくてよいのでT4のデメリットが薄いです。 さてこいつの本当にすごいのは、デバッグすると ちゃんと生成されたコードもステップ実行できちゃうという点です。やばい。 また実行速度も PTTXへ
  • #40: 早いですね
  • #41: これらの理由からSource Generatorは、利用者に特別な理解を求める必要がなく、 再配布しやすいというのがT4比較時のメリットです。 なので、今後はDapperのようなライブラリでも型変換コードの生成を 動的に実行するのではなく、事前にソース生成しておいて利用する形が流行るかもしれません。 早いですからね。 また、Analyzerとの相性が良いので、ユーザーフレンドリーなライブラリを作りやすいのも特徴です。 VSへ 例えば、Identifier属性は1クラス一つと制限しましたが こうやって二つにすると、ちゃんとエラーを出してあげることができます。 PPTX 利用者からすると、使いやすいですよね。 またIDE非依存なのでCI/CDも簡単です。 良いです。
  • #42: 最後にFodyです。 これは曲者ですね VSへ こいつは、C#コードではなくて、ビルドされたあとにDLLを直接改変します。 つまりILをいじります。 ここのコメントの下のコードがILの記述ですが、まぁふつう読めないですよね。 でも実行速度は
  • #43: 早いです。 Source Generatorとかより遅いのは、計測誤差です。
  • #44: 他と比較した場合、Fodyは既存コードの改変が行えるのが最大の特徴です。 そのため、AOPしたい場合なんかで、Source Generatorなんかじゃ実現できなかったことができちゃうのが強みです。 例えば既存コードにトラッキングコードやリトライ実装を埋め込んだりなんかですね。 ただ逆にいうと、ほかの手段で代替できるものは、他の物を利用したほうが良いです。 まずILを覚えなきゃってことで、類似性のない新言語を1つ覚える必要がありますし 同じ実装するにしても、何倍もコードを書かないといけませんし、必要なテストコードも膨れ上がります。 これは、ソース生成であれば1パターンでいいのが、IL生成だと複数パターン実装しないといけないのがその理由です。 たとえば、メンバの値を取得するにしても、メンバを保持しているのが、クラスなのか、ストラクトなのか、Genericなのか メンバがフィールドなのかプロパティなのか、取得対象がクラスなのかストラクトなのか、それによって全部分岐が入るので 累乗的にやることが増えていきます。 多機能だけど、大変。それがFodyです。
  • #45: ということでまとめましょう。
  • #46: いま実はIComparableのボイラーコードを生成するSource Generatorのライブラリを作ってます。 できたら、良かったら使ってください!おしまい
  • #47: ではなくて
  • #48: .NETにはいろんなメタプログラミングの技法があります
  • #49: どれもそれぞれに強みがあって適材適所です 向いているものを向いている場所で使いましょう 特にILをいじるのは最終手段ですよ!
  • #50: ということで、今度こそおしまいです。 ご清聴ありがとうございました。