Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Roslyn とその活用法

neno
September 24, 2022

Roslyn とその活用法

.NET ラボ 2022/09/24 での発表資料

GitHub
- Tapper
- TypedSignalR.Client
- TypedSignalR.Client.TypeScript
Twitter
Blog
neno.dev

neno

September 24, 2022
Tweet

More Decks by neno

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

  3. Roslyn
    2

    View Slide

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

    View Slide

  5. Roslyn
    4
    Compiler API ?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  16. Roslyn
    15
    なにが嬉しいの?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  34. Roslyn
    33
    つくりました

    View Slide

  35. Roslyn
    34
    Roslyn を活用して!

    View Slide

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

    View Slide

  37. 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をアノテーションするだけ

    View Slide

  38. 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 対応

    View Slide

  39. 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 対応

    View Slide

  40. Roslyn : Console App
    39
    Tapper

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  59. 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)
    構文木/意味モデルなどが
    全て含まれている

    View Slide

  60. 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)

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  64. Roslyn: Source Generator
    63
    • IIncrementalGenerator

    View Slide

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

    View Slide

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

    View Slide

  67. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  84. Roslyn
    83
    TypeScript 版も欲しい

    View Slide

  85. 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

    View Slide

  86. 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 を
    アノテーションするだけ

    View Slide

  87. 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 コマンド!

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide