.NET ラボ 2022/09/24 での発表資料
GitHub - Tapper - TypedSignalR.Client - TypedSignalR.Client.TypeScript Twitter Blog neno.dev
Roslyn とその活用法.NET ラボ 2022/09/24何縫ねの。
View Slide
自己紹介1• 所属: NTTコミュニケーションズイノベーションセンター• 趣味: C#, OSS, ドール, 一眼(α7 IV)何縫ねの。nenoNaninunenoMakeブログ https://blog.neno.devその他 https://neno.dev
Roslyn2
Roslyn3• .NET Compiler Platform• Compiler as a Service• C#で書かれた、C#コンパイラ• Compiler API が生えている。https://github.com/dotnet/roslynRoslynC# の観点からみれば
Roslyn4Compiler API ?
Roslyn5コンパイラのお仕事字句解析構文解析意味解析コード生成
Roslyn6コンパイラのお仕事字句解析構文解析意味解析コード生成
Roslyn7コンパイラのお仕事字句解析構文解析意味解析コード生成
Roslyn8コンパイラのお仕事字句解析構文解析意味解析コード生成
Roslyn9コンパイラのお仕事字句解析構文解析意味解析コード生成
Roslyn10コンパイラのお仕事字句解析構文解析意味解析コード生成
Roslyn11コンパイラのお仕事字句解析構文解析意味解析コード生成構文解析時点では、intという文字列
Roslyn12コンパイラのお仕事字句解析構文解析意味解析コード生成文字列ではなく、intという”型”である意味を解析構文解析時点では、intという文字列
Roslyn13コンパイラのお仕事字句解析構文解析意味解析コード生成MSILを出力
Roslyn14字句解析構文解析意味解析コード生成Compiler APIコンパイラのお仕事
Roslyn15なにが嬉しいの?
Roslyn16ソースコードを入力としたプログラムが作れる
• Source Generator• Analyzer• Console AppRoslyn17Compiler APIを通して出来る事。
Roslyn18Compiler APIを通して出来る事。• Source Generator• Analyzer• Console App
• Source Generator• Analyzer• Console AppRoslyn19Compiler APIを通して出来る事。Roslyn 組み込みの機能
• Source Generator• Analyzer• Console AppRoslyn20Compiler APIを通して出来る事。Roslyn 組み込みの機能Roslyn を普通のライブラリとして使用
• Source Generator• Analyzer• Console AppRoslyn21Compiler APIを通して出来る事。Roslyn 組み込みの機能Roslyn を普通のライブラリとして使用活用例(OSS)と共に紹介
• Source Generator• Analyzer• Console AppRoslyn22Compiler APIを通して出来る事。Roslyn 組み込みの機能Roslyn を普通のライブラリとして使用活用例(OSS)と共に紹介
Roslyn : Console App23Server(C#)Client(TypeScript)https://blog.neno.dev/entry/2022/03/31/213837
Roslyn : Console App24Server(C#)Client(TypeScript)https://blog.neno.dev/entry/2022/03/31/213837
Roslyn : Console App25Server(C#)Client(TypeScript)JSONhttps://blog.neno.dev/entry/2022/03/31/213837
Roslyn : Console App26Server(C#)Client(TypeScript)JSONhttps://blog.neno.dev/entry/2022/03/31/213837
Roslyn : Console App27Server(C#)Client(TypeScript)JSONhttps://blog.neno.dev/entry/2022/03/31/213837両方手書き?
Roslyn : Console App28Server(C#)Client(TypeScript)JSONServer側を変更したらClient側はコンパイルエラー吐いて欲しいhttps://blog.neno.dev/entry/2022/03/31/213837両方手書き?
Roslyn : Console App29JSON両方手書き?Server(C#)Client(TypeScript)https://blog.neno.dev/entry/2022/03/31/213837Server側を変更したらClient側はコンパイルエラー吐いて欲しい
Roslyn : Console App30JSON両方手書き?Client(TypeScript)Server(C#)https://blog.neno.dev/entry/2022/03/31/213837Server側を変更したらClient側はコンパイルエラー吐いて欲しい
Roslyn : Console App31JSONClient(TypeScript)Server(C#)
Roslyn : Console App32JSONC#からTypeScriptにトランスパイルできればいいのでは?Server(C#)Client(TypeScript)
Roslyn33つくりました
Roslyn34Roslyn を活用して!
Roslyn : Console App35• C#の型定義からTypeScriptの型定義を生成するConsole App• Roslynで意味解析まで行い、対象の型の名前、メンバの型/名前等を読み取ってTypeScriptコードを生成。• シリアライザによって生成する型を適切に調整$ tapper --project path/to/Xxx.csproj --output generatedhttps://github.com/nenoNaninu/TapperTapper
Roslyn : Console App36• C#の型定義からTypeScriptの型定義を生成するConsole App• Roslynで意味解析まで行い、対象の型の名前、メンバの型/名前等を読み取ってTypeScriptコードを生成。• シリアライザによって生成する型を適切に調整$ tapper --project path/to/Xxx.csproj --output generatedhttps://github.com/nenoNaninu/TapperTapperAttributeをアノテーションするだけ
Roslyn : Console App37• C#の型定義からTypeScriptの型定義を生成するConsole App• Roslynで意味解析まで行い、対象の型の名前、メンバの型/名前等を読み取ってTypeScriptコードを生成。• シリアライザによって生成する型を適切に調整$ tapper --project path/to/Xxx.csproj --output generatedhttps://github.com/nenoNaninu/TapperTapperAttributeをアノテーションするだけJSON/MsgPack 対応
Roslyn : Console App38• C#の型定義からTypeScriptの型定義を生成するConsole App• Roslynで意味解析まで行い、対象の型の名前、メンバの型/名前等を読み取ってTypeScriptコードを生成。• シリアライザによって生成する型を適切に調整$ tapper --project path/to/Xxx.csproj --output generatedhttps://github.com/nenoNaninu/TapperTapperAttributeをアノテーションするだけ1コマンド!JSON/MsgPack 対応
Roslyn : Console App39Tapper
Roslyn : Console App40詳細な JSDocどの C# 型由来か一目瞭然Tapper
Roslyn : Console App41詳細な JSDocどの C# 型由来か一目瞭然Tapper TypeScript での number がint か float かで迷わない!
Roslyn : Console App42• ユーザー定義型がネストしたら?Tapperhttps://github.com/nenoNaninu/Tapper
Roslyn : Console App43• ユーザー定義型がネストしたら?Tapperhttps://github.com/nenoNaninu/TapperAttributeがアノテーションされてる型の中でユーザ定義型が使われている
Roslyn : Console App44• ユーザー定義型がネストしたら?Tapperhttps://github.com/nenoNaninu/TapperAttributeがアノテーションされてる型の中でユーザ定義型が使われている中で使われる型にもAttributeをアノテーションする必要がある
Roslyn : Console App45• ユーザー定義型がネストしたら?Tapperhttps://github.com/nenoNaninu/TapperAttributeがアノテーションされてる型の中でユーザ定義型が使われている中で使われる型にもAttributeをアノテーションする必要があるAttributeなのでコンパイル時に制約出来ない。→ランタイムエラー?
• Source Generator• Analyzer• Console AppRoslyn46Compiler APIを通して出来る事。
• Source Generator• Analyzer• Console AppRoslyn47Compiler APIを通して出来る事。
Roslyn: Analyzer48Attribute をアノテーションするのが正しい使い方
Roslyn: Analyzer49Attribute をアノテーションするのが正しい使い方
Roslyn: Analyzer50Attribute をアノテーションするのが正しい使い方メンバの型にAttribute が付いてないぞ~と教えてくれる
Roslyn: Analyzer51Attribute をアノテーションするのが正しい使い方メンバの型にAttribute が付いてないぞ~と教えてくれるCLI で実行時エラー!みたいな状況を防げる
• Source Generator• Analyzer• Console AppRoslyn52Compiler APIを通して出来る事。
• Source Generator• Analyzer• Console AppRoslyn53Compiler APIを通して出来る事。
Roslyn: Source Generator54字句解析構文解析意味解析コード生成Source Generator とは
Roslyn: Source Generator55字句解析構文解析意味解析コード生成Source Generator とはCompilationを取得
Roslyn: Source Generator56字句解析構文解析意味解析コード生成Source Generator とはCompilationを取得構文木/意味モデルなどが全て含まれている
Roslyn: Source Generator57字句解析構文解析意味解析コード生成Source Generator とはhttps://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overviewCompilationを取得構文木/意味モデルなどが全て含まれている
Roslyn: Source Generator58字句解析構文解析意味解析コード生成Source Generator とはCompilationを取得https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview動的コード生成IL GeneratorExpression Tree(Reflection)構文木/意味モデルなどが全て含まれている
Roslyn: Source Generator59字句解析構文解析意味解析コード生成Source Generator とはCompilationを取得https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview構文木/意味モデルなどが全て含まれている8割くらいはSource Generator で代替動的コード生成IL GeneratorExpression Tree(Reflection)
Roslyn: Source Generator60• interface ISourceGenerator• 全ソースコードを探索→ 必要箇所を収集→ コード生成.NET 5.NET 6• interface IIncrementalGenerator• ソースコードの変更を検知→ 変更箇所が必要箇所か検査→ 必要箇所だった場合のみコード生成のロジックが発火
Roslyn: Source Generator61• interface ISourceGenerator• 全ソースコードを探索→ 必要箇所を収集→ コード生成.NET 5.NET 6• interface IIncrementalGenerator• ソースコードの変更を検知→ 変更箇所が必要箇所か検査→ 必要箇所だった場合のみコード生成のロジックが発火IDE で一文字変更毎に全ソース探索/コード生成が実行され開発体験が厳しかった
Roslyn: Source Generator62• interface ISourceGenerator• 全ソースコードを探索→ 必要箇所を収集→ コード生成.NET 5.NET 6• interface IIncrementalGenerator• ソースコードの変更を検知→ 変更箇所が必要箇所か検査→ 必要箇所だった場合のみコード生成のロジックが発火IDE で一文字変更毎に全ソース探索/コード生成が実行され開発体験が厳しかったIDE フレンドリー!書き心地も向上!
Roslyn: Source Generator63• IIncrementalGenerator
Roslyn: Source Generator64• IIncrementalGenerator①変更箇所のコード生成に必要かの検証②実際にコード生成するための処理のパイプラインを Rx ライクに記述
Roslyn: Source Generator65• IIncrementalGenerator①変更箇所のコード生成に必要かの検証②実際にコード生成するための処理のパイプラインを Rx ライクに記述詳細は後述
Roslyn: Source Generator66• 双方向のリアルタイム通信機能を提供する 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
Roslyn: Source Generator67• Hubの method をinvokeする際 / Client の method を登録をする際、• ①method 指定が文字列で辛い。• ②引数/返り値の型を手動で与える必要があり辛い。SignalR の問題点https://github.com/dotnet/aspnetcore/tree/main/src/SignalR
Roslyn: Source Generator68SignalR の問題点https://github.com/dotnet/aspnetcore/tree/main/src/SignalRタイポ怖い変更に追従させるの大変• Hubの method をinvokeする際 / Client の method を登録をする際、• ①method 指定が文字列で辛い。• ②引数/返り値の型を手動で与える必要があり辛い。
Roslyn: Source Generator69SignalR の問題点https://github.com/dotnet/aspnetcore/tree/main/src/SignalRタイポ怖い変更に追従させるの大変Source Generator を活用して解決!• Hubの method をinvokeする際 / Client の method を登録をする際、• ①method 指定が文字列で辛い。• ②引数/返り値の型を手動で与える必要があり辛い。
Roslyn: Source Generator70TypedSignalR.Clienthttps://github.com/nenoNaninu/TypedSignalR.Client
Roslyn: Source Generator71TypedSignalR.Clientserver 側は interface で強く型付け出来る。一方 client 側は…https://github.com/nenoNaninu/TypedSignalR.Client
Roslyn: Source Generator72TypedSignalR.ClientBeforehttps://github.com/nenoNaninu/TypedSignalR.Clientserver 側は interface で強く型付け出来る。一方 client 側は…
Roslyn: Source Generator73TypedSignalR.ClientBeforehttps://github.com/nenoNaninu/TypedSignalR.Clientserver 側は interface で強く型付け出来る。一方 client 側は…
Roslyn: Source Generator74TypedSignalR.ClientBeforeAfterhttps://github.com/nenoNaninu/TypedSignalR.Clientserver 側は interface で強く型付け出来る。一方 client 側は…
Roslyn: Source Generator75TypedSignalR.ClientBeforeAfterhttps://github.com/nenoNaninu/TypedSignalR.Client型引数に渡された interface を実装した class を内部で生成server 側は interface で強く型付け出来る。一方 client 側は…
Roslyn: Source Generator76TypedSignalR.ClientBeforeAfter型引数に渡された interface で定義されている全methodをconnection に bind するコードを内部で生成https://github.com/nenoNaninu/TypedSignalR.Client型引数に渡された interface を実装した class を内部で生成server 側は interface で強く型付け出来る。一方 client 側は…
Roslyn: Source Generator77• 仕様に従ってない場合• 型引数に interface 以外を渡している• interfaceに property,不正な返り値/引数のmethod 等TypedSignalR.Clientコンパイルエラー!詳細なエラーメッセージライブラリの仕様を知らなくても正しく使える!さらば ランタイムエラー!
Roslyn: Source Generator78https://github.com/nenoNaninu/TypedSignalR.Client/blob/v3.1.1/src/TypedSignalR.Client/SourceGenerator.cs
Roslyn: Source Generator79https://github.com/nenoNaninu/TypedSignalR.Client/blob/v3.1.1/src/TypedSignalR.Client/SourceGenerator.cs事前に必要なコードを一度だけ生成TypedSignalR.Client の場合は拡張メソッドなど
Roslyn: Source Generator80事前に必要なコードを一度だけ生成TypedSignalR.Client の場合は拡張メソッドなど変更箇所に対する検証/整形などのパイプラインを Rx ライクに記述https://github.com/nenoNaninu/TypedSignalR.Client/blob/v3.1.1/src/TypedSignalR.Client/SourceGenerator.cs
Roslyn: Source Generator81事前に必要なコードを一度だけ生成TypedSignalR.Client の場合は拡張メソッドなど変更箇所に対する検証/整形などのパイプラインを Rx ライクに記述https://github.com/nenoNaninu/TypedSignalR.Client/blob/v3.1.1/src/TypedSignalR.Client/SourceGenerator.cs構築したパイプラインを使ってコードを生成/登録
Roslyn82TypedSignalR.ClientC# の SignalR client のためのもの
Roslyn83TypeScript 版も欲しい
Roslyn: Console App + Code Analyzer84• C#のinterfaceを解析→ TypeScriptで強く型付けされたSignalR clientを提供TypedSignalR.Client.TypeScript$ dotnet tsrts --project path/to/Xxx.csproj --output generatedhttps://github.com/nenoNaninu/TypedSignalR.Client.TypeScript
Roslyn: Console App + Code Analyzer85• C#のinterfaceを解析→ TypeScriptで強く型付けされたSignalR clientを提供TypedSignalR.Client.TypeScript$ dotnet tsrts --project path/to/Xxx.csproj --output generatedhttps://github.com/nenoNaninu/TypedSignalR.Client.TypeScriptAttribute をアノテーションするだけAttribute をアノテーションするだけ
Roslyn: Console App + Code Analyzer86• C#のinterfaceを解析→ TypeScriptで強く型付けされたSignalR clientを提供TypedSignalR.Client.TypeScript$ dotnet tsrts --project path/to/Xxx.csproj --output generatedhttps://github.com/nenoNaninu/TypedSignalR.Client.TypeScriptAttribute をアノテーションするだけAttribute をアノテーションするだけ1 コマンド!
Roslyn: Console App + Code Analyzer87TypedSignalR.Client.TypeScriptBefore
Roslyn: Console App + Code Analyzer88TypedSignalR.Client.TypeScriptBeforeAfter
Roslyn: Console App + Code Analyzer89TypedSignalR.Client.TypeScriptBeforeAfter
Roslyn: Console App + Code Analyzer90TypedSignalR.Client.TypeScriptBeforeAfter 型指定さえすればテンプレートはエディタが吐き出してくれる
Roslyn: Console App + Code Analyzer91TypedSignalR.Client.TypeScriptBeforeAfter 型指定さえすればテンプレートはエディタが吐き出してくれる文字列つかってるじゃん?
Roslyn: Console App + Code Analyzer92TypedSignalR.Client.TypeScriptBeforeAfter文字列つかってるじゃん?
Roslyn: Console App + Code Analyzer93TypedSignalR.Client.TypeScriptBeforeAfter文字列つかってるじゃん?string literal type でoverload してるので大丈夫。Intellisense もバッチリ
Roslyn: Console App + Code Analyzer94メソッド定義に Tapper のAttribute が付いてない型を使っていた場合に警告https://github.com/nenoNaninu/TypedSignalR.Client.TypeScript#analyzerTypedSignalR.Client.TypeScript はTapper 内蔵してこれを解決SignalR の hub/receiver method でユーザ定義型を使いたい
まとめ95• Source Generator• C# コードを解析してC# コードを生成!以下の要望をストレートに解決!• 動的コード生成しないと厳しいな~• ボイラーコード大量に発生するな~• Analyzer• 型で制約できない事はAnalyzerで制約!リアルタイムに発見!• Tapperのように外部ツール都合のAttributeの使い方を矯正• このAPI使うな!この返り値を捨てるな!とか、使い道はいろいろ。• 頑張れば code fix も• Console App• C# コードと連携する他言語(TypeScript等)をより快適に!• ぶっちゃけなんでもできます!やりたい放題!Roslyn を使う事で C# コードを入力として、アレコレ出来る!