Slide 1

Slide 1 text

Roslyn とその活用法 .NET ラボ 2022/09/24 何縫ねの。

Slide 2

Slide 2 text

自己紹介 1 • 所属: NTTコミュニケーションズ イノベーションセンター • 趣味: C#, OSS, ドール, 一眼(α7 IV) 何縫ねの。 nenoNaninu nenoMake ブログ https://blog.neno.dev その他 https://neno.dev

Slide 3

Slide 3 text

Roslyn 2

Slide 4

Slide 4 text

Roslyn 3 • .NET Compiler Platform • Compiler as a Service • C#で書かれた、C#コンパイラ • Compiler API が生えている。 https://github.com/dotnet/roslyn Roslyn C# の観点からみれば

Slide 5

Slide 5 text

Roslyn 4 Compiler API ?

Slide 6

Slide 6 text

Roslyn 5 コンパイラのお仕事 字句解析 構文解析 意味解析 コード生成

Slide 7

Slide 7 text

Roslyn 6 コンパイラのお仕事 字句解析 構文解析 意味解析 コード生成

Slide 8

Slide 8 text

Roslyn 7 コンパイラのお仕事 字句解析 構文解析 意味解析 コード生成

Slide 9

Slide 9 text

Roslyn 8 コンパイラのお仕事 字句解析 構文解析 意味解析 コード生成

Slide 10

Slide 10 text

Roslyn 9 コンパイラのお仕事 字句解析 構文解析 意味解析 コード生成

Slide 11

Slide 11 text

Roslyn 10 コンパイラのお仕事 字句解析 構文解析 意味解析 コード生成

Slide 12

Slide 12 text

Roslyn 11 コンパイラのお仕事 字句解析 構文解析 意味解析 コード生成 構文解析時点では、 intという文字列

Slide 13

Slide 13 text

Roslyn 12 コンパイラのお仕事 字句解析 構文解析 意味解析 コード生成 文字列ではなく、 intという”型”である意味を解析 構文解析時点では、 intという文字列

Slide 14

Slide 14 text

Roslyn 13 コンパイラのお仕事 字句解析 構文解析 意味解析 コード生成 MSILを出力

Slide 15

Slide 15 text

Roslyn 14 字句解析 構文解析 意味解析 コード生成 Compiler API コンパイラのお仕事

Slide 16

Slide 16 text

Roslyn 15 なにが嬉しいの?

Slide 17

Slide 17 text

Roslyn 16 ソースコードを入力とした プログラムが作れる

Slide 18

Slide 18 text

• Source Generator • Analyzer • Console App Roslyn 17 Compiler APIを通して出来る事。

Slide 19

Slide 19 text

Roslyn 18 Compiler APIを通して出来る事。 • Source Generator • Analyzer • Console App

Slide 20

Slide 20 text

• Source Generator • Analyzer • Console App Roslyn 19 Compiler APIを通して出来る事。 Roslyn 組み込みの機能

Slide 21

Slide 21 text

• Source Generator • Analyzer • Console App Roslyn 20 Compiler APIを通して出来る事。 Roslyn 組み込みの機能 Roslyn を普通の ライブラリとして使用

Slide 22

Slide 22 text

• Source Generator • Analyzer • Console App Roslyn 21 Compiler APIを通して出来る事。 Roslyn 組み込みの機能 Roslyn を普通の ライブラリとして使用 活用例(OSS)と共に紹介

Slide 23

Slide 23 text

• Source Generator • Analyzer • Console App Roslyn 22 Compiler APIを通して出来る事。 Roslyn 組み込みの機能 Roslyn を普通の ライブラリとして使用 活用例(OSS)と共に紹介

Slide 24

Slide 24 text

Roslyn : Console App 23 Server (C#) Client (TypeScript) https://blog.neno.dev/entry/2022/03/31/213837

Slide 25

Slide 25 text

Roslyn : Console App 24 Server (C#) Client (TypeScript) https://blog.neno.dev/entry/2022/03/31/213837

Slide 26

Slide 26 text

Roslyn : Console App 25 Server (C#) Client (TypeScript) JSON https://blog.neno.dev/entry/2022/03/31/213837

Slide 27

Slide 27 text

Roslyn : Console App 26 Server (C#) Client (TypeScript) JSON https://blog.neno.dev/entry/2022/03/31/213837

Slide 28

Slide 28 text

Roslyn : Console App 27 Server (C#) Client (TypeScript) JSON https://blog.neno.dev/entry/2022/03/31/213837 両方手書き?

Slide 29

Slide 29 text

Roslyn : Console App 28 Server (C#) Client (TypeScript) JSON Server側を変更したらClient側は コンパイルエラー吐いて欲しい https://blog.neno.dev/entry/2022/03/31/213837 両方手書き?

Slide 30

Slide 30 text

Roslyn : Console App 29 JSON 両方手書き? Server (C#) Client (TypeScript) https://blog.neno.dev/entry/2022/03/31/213837 Server側を変更したらClient側は コンパイルエラー吐いて欲しい

Slide 31

Slide 31 text

Roslyn : Console App 30 JSON 両方手書き? Client (TypeScript) Server (C#) https://blog.neno.dev/entry/2022/03/31/213837 Server側を変更したらClient側は コンパイルエラー吐いて欲しい

Slide 32

Slide 32 text

Roslyn : Console App 31 JSON Client (TypeScript) Server (C#)

Slide 33

Slide 33 text

Roslyn : Console App 32 JSON C#からTypeScriptに トランスパイルできればいいのでは? Server (C#) Client (TypeScript)

Slide 34

Slide 34 text

Roslyn 33 つくりました

Slide 35

Slide 35 text

Roslyn 34 Roslyn を活用して!

Slide 36

Slide 36 text

Roslyn : Console App 35 • C#の型定義からTypeScriptの型定義を 生成するConsole App • Roslynで意味解析まで行い、対象の型の 名前、メンバの型/名前等を 読み取ってTypeScriptコードを生成。 • シリアライザによって生成する型を 適切に調整 $ tapper --project path/to/Xxx.csproj --output generated https://github.com/nenoNaninu/Tapper Tapper

Slide 37

Slide 37 text

Roslyn : Console App 36 • C#の型定義からTypeScriptの型定義を 生成するConsole App • Roslynで意味解析まで行い、対象の型の 名前、メンバの型/名前等を 読み取ってTypeScriptコードを生成。 • シリアライザによって生成する型を 適切に調整 $ tapper --project path/to/Xxx.csproj --output generated https://github.com/nenoNaninu/Tapper Tapper Attributeをアノテーションするだけ

Slide 38

Slide 38 text

Roslyn : Console App 37 • C#の型定義からTypeScriptの型定義を 生成するConsole App • Roslynで意味解析まで行い、対象の型の 名前、メンバの型/名前等を 読み取ってTypeScriptコードを生成。 • シリアライザによって生成する型を 適切に調整 $ tapper --project path/to/Xxx.csproj --output generated https://github.com/nenoNaninu/Tapper Tapper Attributeをアノテーションするだけ JSON/MsgPack 対応

Slide 39

Slide 39 text

Roslyn : Console App 38 • C#の型定義からTypeScriptの型定義を 生成するConsole App • Roslynで意味解析まで行い、対象の型の 名前、メンバの型/名前等を 読み取ってTypeScriptコードを生成。 • シリアライザによって生成する型を 適切に調整 $ tapper --project path/to/Xxx.csproj --output generated https://github.com/nenoNaninu/Tapper Tapper Attributeをアノテーションするだけ 1コマンド! JSON/MsgPack 対応

Slide 40

Slide 40 text

Roslyn : Console App 39 Tapper

Slide 41

Slide 41 text

Roslyn : Console App 40 詳細な JSDoc どの C# 型由来か一目瞭然 Tapper

Slide 42

Slide 42 text

Roslyn : Console App 41 詳細な JSDoc どの C# 型由来か一目瞭然 Tapper TypeScript での number が int か float かで迷わない!

Slide 43

Slide 43 text

Roslyn : Console App 42 • ユーザー定義型がネストしたら? Tapper https://github.com/nenoNaninu/Tapper

Slide 44

Slide 44 text

Roslyn : Console App 43 • ユーザー定義型がネストしたら? Tapper https://github.com/nenoNaninu/Tapper Attributeがアノテーション されてる型の中で ユーザ定義型が使われている

Slide 45

Slide 45 text

Roslyn : Console App 44 • ユーザー定義型がネストしたら? Tapper https://github.com/nenoNaninu/Tapper Attributeがアノテーション されてる型の中で ユーザ定義型が使われている 中で使われる型にも Attributeを アノテーションする 必要がある

Slide 46

Slide 46 text

Roslyn : Console App 45 • ユーザー定義型がネストしたら? Tapper https://github.com/nenoNaninu/Tapper Attributeがアノテーション されてる型の中で ユーザ定義型が使われている 中で使われる型にも Attributeを アノテーションする 必要がある Attributeなので コンパイル時に 制約出来ない。 →ランタイムエラー?

Slide 47

Slide 47 text

• Source Generator • Analyzer • Console App Roslyn 46 Compiler APIを通して出来る事。

Slide 48

Slide 48 text

• Source Generator • Analyzer • Console App Roslyn 47 Compiler APIを通して出来る事。

Slide 49

Slide 49 text

Roslyn: Analyzer 48 Attribute を アノテーションするのが 正しい使い方

Slide 50

Slide 50 text

Roslyn: Analyzer 49 Attribute を アノテーションするのが 正しい使い方

Slide 51

Slide 51 text

Roslyn: Analyzer 50 Attribute を アノテーションするのが 正しい使い方 メンバの型に Attribute が付いてないぞ~ と教えてくれる

Slide 52

Slide 52 text

Roslyn: Analyzer 51 Attribute を アノテーションするのが 正しい使い方 メンバの型に Attribute が付いてないぞ~ と教えてくれる CLI で実行時エラー! みたいな状況を防げる

Slide 53

Slide 53 text

• Source Generator • Analyzer • Console App Roslyn 52 Compiler APIを通して出来る事。

Slide 54

Slide 54 text

• Source Generator • Analyzer • Console App Roslyn 53 Compiler APIを通して出来る事。

Slide 55

Slide 55 text

Roslyn: Source Generator 54 字句解析 構文解析 意味解析 コード生成 Source Generator とは

Slide 56

Slide 56 text

Roslyn: Source Generator 55 字句解析 構文解析 意味解析 コード生成 Source Generator とは Compilation を取得

Slide 57

Slide 57 text

Roslyn: Source Generator 56 字句解析 構文解析 意味解析 コード生成 Source Generator とは Compilation を取得 構文木/意味モデルなどが 全て含まれている

Slide 58

Slide 58 text

Roslyn: Source Generator 57 字句解析 構文解析 意味解析 コード生成 Source Generator とは https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview Compilation を取得 構文木/意味モデルなどが 全て含まれている

Slide 59

Slide 59 text

Roslyn: Source Generator 58 字句解析 構文解析 意味解析 コード生成 Source Generator とは Compilation を取得 https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview 動的コード生成 IL Generator Expression Tree (Reflection) 構文木/意味モデルなどが 全て含まれている

Slide 60

Slide 60 text

Roslyn: Source Generator 59 字句解析 構文解析 意味解析 コード生成 Source Generator とは Compilation を取得 https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview 構文木/意味モデルなどが 全て含まれている 8割くらいは Source Generator で代替 動的コード生成 IL Generator Expression Tree (Reflection)

Slide 61

Slide 61 text

Roslyn: Source Generator 60 • interface ISourceGenerator • 全ソースコードを探索 → 必要箇所を収集 → コード生成 .NET 5 .NET 6 • interface IIncrementalGenerator • ソースコードの変更を検知 → 変更箇所が必要箇所か検査 → 必要箇所だった場合のみ コード生成のロジックが発火

Slide 62

Slide 62 text

Roslyn: Source Generator 61 • interface ISourceGenerator • 全ソースコードを探索 → 必要箇所を収集 → コード生成 .NET 5 .NET 6 • interface IIncrementalGenerator • ソースコードの変更を検知 → 変更箇所が必要箇所か検査 → 必要箇所だった場合のみ コード生成のロジックが発火 IDE で一文字変更毎に 全ソース探索/コード生成が実行され 開発体験が厳しかった

Slide 63

Slide 63 text

Roslyn: Source Generator 62 • interface ISourceGenerator • 全ソースコードを探索 → 必要箇所を収集 → コード生成 .NET 5 .NET 6 • interface IIncrementalGenerator • ソースコードの変更を検知 → 変更箇所が必要箇所か検査 → 必要箇所だった場合のみ コード生成のロジックが発火 IDE で一文字変更毎に 全ソース探索/コード生成が実行され 開発体験が厳しかった IDE フレンドリー! 書き心地も向上!

Slide 64

Slide 64 text

Roslyn: Source Generator 63 • IIncrementalGenerator

Slide 65

Slide 65 text

Roslyn: Source Generator 64 • IIncrementalGenerator ①変更箇所のコード生成に必要かの検証 ②実際にコード生成するための処理 のパイプラインを Rx ライクに記述

Slide 66

Slide 66 text

Roslyn: Source Generator 65 • IIncrementalGenerator ①変更箇所のコード生成に必要かの検証 ②実際にコード生成するための処理 のパイプラインを Rx ライクに記述 詳細は後述

Slide 67

Slide 67 text

Roslyn: Source Generator 66 • 双方向のリアルタイム通信機能を提供する RPC ライブラリ • WebSocket が繋がらなくても大丈夫! • .NET Core移行時に実装を大幅に改良、現在も機能追加がされ続けている。 • 認証認可が .NET Core 3.1 で組み込まれたり。.NET 7 でも新機能が予定されてる。 • ASP.NET Core 組み込み。 • リポジトリ的にも ASP.NET Core の中。 • Blazor Server とかでも使われている。 SignalR とは https://github.com/dotnet/aspnetcore/tree/main/src/SignalR

Slide 68

Slide 68 text

Roslyn: Source Generator 67 • Hubの method をinvokeする際 / Client の method を登録をする際、 • ①method 指定が文字列で辛い。 • ②引数/返り値の型を手動で与える必要があり辛い。 SignalR の問題点 https://github.com/dotnet/aspnetcore/tree/main/src/SignalR

Slide 69

Slide 69 text

Roslyn: Source Generator 68 SignalR の問題点 https://github.com/dotnet/aspnetcore/tree/main/src/SignalR タイポ怖い 変更に追従させるの大変 • Hubの method をinvokeする際 / Client の method を登録をする際、 • ①method 指定が文字列で辛い。 • ②引数/返り値の型を手動で与える必要があり辛い。

Slide 70

Slide 70 text

Roslyn: Source Generator 69 SignalR の問題点 https://github.com/dotnet/aspnetcore/tree/main/src/SignalR タイポ怖い 変更に追従させるの大変 Source Generator を活用して解決! • Hubの method をinvokeする際 / Client の method を登録をする際、 • ①method 指定が文字列で辛い。 • ②引数/返り値の型を手動で与える必要があり辛い。

Slide 71

Slide 71 text

Roslyn: Source Generator 70 TypedSignalR.Client https://github.com/nenoNaninu/TypedSignalR.Client

Slide 72

Slide 72 text

Roslyn: Source Generator 71 TypedSignalR.Client server 側は interface で 強く型付け出来る。 一方 client 側は… https://github.com/nenoNaninu/TypedSignalR.Client

Slide 73

Slide 73 text

Roslyn: Source Generator 72 TypedSignalR.Client Before https://github.com/nenoNaninu/TypedSignalR.Client server 側は interface で 強く型付け出来る。 一方 client 側は…

Slide 74

Slide 74 text

Roslyn: Source Generator 73 TypedSignalR.Client Before https://github.com/nenoNaninu/TypedSignalR.Client server 側は interface で 強く型付け出来る。 一方 client 側は…

Slide 75

Slide 75 text

Roslyn: Source Generator 74 TypedSignalR.Client Before After https://github.com/nenoNaninu/TypedSignalR.Client server 側は interface で 強く型付け出来る。 一方 client 側は…

Slide 76

Slide 76 text

Roslyn: Source Generator 75 TypedSignalR.Client Before After https://github.com/nenoNaninu/TypedSignalR.Client 型引数に渡された interface を 実装した class を内部で生成 server 側は interface で 強く型付け出来る。 一方 client 側は…

Slide 77

Slide 77 text

Roslyn: Source Generator 76 TypedSignalR.Client Before After 型引数に渡された interface で 定義されている全methodを connection に bind する コードを内部で生成 https://github.com/nenoNaninu/TypedSignalR.Client 型引数に渡された interface を 実装した class を内部で生成 server 側は interface で 強く型付け出来る。 一方 client 側は…

Slide 78

Slide 78 text

Roslyn: Source Generator 77 • 仕様に従ってない場合 • 型引数に interface 以外を渡している • interfaceに property, 不正な返り値/引数のmethod 等 TypedSignalR.Client コンパイルエラー! 詳細なエラーメッセージ ライブラリの仕様を知らなくても正しく使える! さらば ランタイムエラー!

Slide 79

Slide 79 text

Roslyn: Source Generator 78 https://github.com/nenoNaninu/TypedSignalR.Client/blob/v3.1.1/src/TypedSignalR.Client/SourceGenerator.cs

Slide 80

Slide 80 text

Roslyn: Source Generator 79 https://github.com/nenoNaninu/TypedSignalR.Client/blob/v3.1.1/src/TypedSignalR.Client/SourceGenerator.cs 事前に必要なコードを一度だけ生成 TypedSignalR.Client の場合は 拡張メソッドなど

Slide 81

Slide 81 text

Roslyn: Source Generator 80 事前に必要なコードを一度だけ生成 TypedSignalR.Client の場合は 拡張メソッドなど 変更箇所に対する検証/整形などの パイプラインを Rx ライクに記述 https://github.com/nenoNaninu/TypedSignalR.Client/blob/v3.1.1/src/TypedSignalR.Client/SourceGenerator.cs

Slide 82

Slide 82 text

Roslyn: Source Generator 81 事前に必要なコードを一度だけ生成 TypedSignalR.Client の場合は 拡張メソッドなど 変更箇所に対する検証/整形などの パイプラインを Rx ライクに記述 https://github.com/nenoNaninu/TypedSignalR.Client/blob/v3.1.1/src/TypedSignalR.Client/SourceGenerator.cs 構築したパイプラインを使って コードを生成/登録

Slide 83

Slide 83 text

Roslyn 82 TypedSignalR.Client C# の SignalR client のためのもの

Slide 84

Slide 84 text

Roslyn 83 TypeScript 版も欲しい

Slide 85

Slide 85 text

Roslyn: Console App + Code Analyzer 84 • C#のinterfaceを解析 → TypeScriptで強く型付けされた SignalR clientを提供 TypedSignalR.Client.TypeScript $ dotnet tsrts --project path/to/Xxx.csproj --output generated https://github.com/nenoNaninu/TypedSignalR.Client.TypeScript

Slide 86

Slide 86 text

Roslyn: Console App + Code Analyzer 85 • C#のinterfaceを解析 → TypeScriptで強く型付けされた SignalR clientを提供 TypedSignalR.Client.TypeScript $ dotnet tsrts --project path/to/Xxx.csproj --output generated https://github.com/nenoNaninu/TypedSignalR.Client.TypeScript Attribute を アノテーションするだけ Attribute を アノテーションするだけ

Slide 87

Slide 87 text

Roslyn: Console App + Code Analyzer 86 • C#のinterfaceを解析 → TypeScriptで強く型付けされた SignalR clientを提供 TypedSignalR.Client.TypeScript $ dotnet tsrts --project path/to/Xxx.csproj --output generated https://github.com/nenoNaninu/TypedSignalR.Client.TypeScript Attribute を アノテーションするだけ Attribute を アノテーションするだけ 1 コマンド!

Slide 88

Slide 88 text

Roslyn: Console App + Code Analyzer 87 TypedSignalR.Client.TypeScript Before

Slide 89

Slide 89 text

Roslyn: Console App + Code Analyzer 88 TypedSignalR.Client.TypeScript Before After

Slide 90

Slide 90 text

Roslyn: Console App + Code Analyzer 89 TypedSignalR.Client.TypeScript Before After

Slide 91

Slide 91 text

Roslyn: Console App + Code Analyzer 90 TypedSignalR.Client.TypeScript Before After 型指定さえすれば テンプレートはエディタ が吐き出してくれる

Slide 92

Slide 92 text

Roslyn: Console App + Code Analyzer 91 TypedSignalR.Client.TypeScript Before After 型指定さえすれば テンプレートはエディタ が吐き出してくれる 文字列 つかってるじゃん?

Slide 93

Slide 93 text

Roslyn: Console App + Code Analyzer 92 TypedSignalR.Client.TypeScript Before After 文字列 つかってるじゃん?

Slide 94

Slide 94 text

Roslyn: Console App + Code Analyzer 93 TypedSignalR.Client.TypeScript Before After 文字列 つかってるじゃん? string literal type で overload してるので大丈夫。 Intellisense もバッチリ

Slide 95

Slide 95 text

Roslyn: Console App + Code Analyzer 94 メソッド定義に Tapper の Attribute が付いてない型を 使っていた場合に警告 https://github.com/nenoNaninu/TypedSignalR.Client.TypeScript#analyzer TypedSignalR.Client.TypeScript は Tapper 内蔵してこれを解決 SignalR の hub/receiver method で ユーザ定義型を使いたい

Slide 96

Slide 96 text

まとめ 95 • Source Generator • C# コードを解析してC# コードを生成!以下の要望をストレートに解決! • 動的コード生成しないと厳しいな~ • ボイラーコード大量に発生するな~ • Analyzer • 型で制約できない事はAnalyzerで制約!リアルタイムに発見! • Tapperのように外部ツール都合のAttributeの使い方を矯正 • このAPI使うな!この返り値を捨てるな!とか、使い道はいろいろ。 • 頑張れば code fix も • Console App • C# コードと連携する他言語(TypeScript等)をより快適に! • ぶっちゃけなんでもできます!やりたい放題! Roslyn を使う事で C# コードを入力として、アレコレ出来る!