インターセプター RiderとReSharperでC# 12を使用する

本記事はJetBrains公式ブログより引用した内容となります。

## インターセプター RiderとReSharperでC# 12を使用する
C# 12の言語機能と、ReSharper と Rider によってコードベースに簡単に導入できる仕組みについて詳しく見ていきます。まだの場合は、最新の.NET 8 SDKをダウンロードし、プロジェクトファイルを更新してください！

インターセプターはC# 12でプレビュー版として利用可能な実験的な機能です。将来のリリースで変更される可能性や削除される可能性があることに注意してください。

## 背景と構文
インターセプターは、こうだなソースジェネレーターのシナリオ、特にAOTコンパイル(Ahead-of-Timeコンパイル)で使用されることを意図しています。現在、インターセプターは ASP.NET Core の最小限の API で使用され、HTTP 要求のリフレクション ベースのマッピングをよりカスタマイズされた効率的な実装に置き換えています。

。インターセプターは、特定のメソッド呼び出しを、ユーザーコードに要求を加えることなく代替物にリダイレクトできるようにします。この技術により、ソースジェネレーターの作成者は、非効率的なコードパス（例えば、リフレクション）をシームレスに排除し、コンパイル時に手作りの特化した実装に置き換えることが可能になります。

インターセプターをプロジェクトで使用する前に、いくつかのプロパティを設定する必要があります。
![dotnet_howtos_20240117-5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936166/c34ace3e-50d5-6843-8464-fbc8c243af24.png)

`InterceptorsPreviewNamespaces`プロパティは、インターセプターを含めることができる名前空間を定義し、一定のレベルの認識を提供します。`Features`プロパティは、以前は .NET 8 プレビューでインターセプター機能を有効にするために使用されていましたが、現在は必要ありません。

では、簡単な例を見てみましょう。
```cs
// User code
Console.WriteLine("original"); // 🥱
Console.WriteLine("original"); // 🤯

// Generated code
namespace CSharp12
{
    public static class Interceptor
    {
        [System.Runtime.CompilerServices.InterceptsLocation(
            // TODO: Update absolute file path
            filePath: "/path/to/file",
                        // TODO: Point to 'Console.{HERE}WriteLine(...)'
            line: 3,
            column: 9)]
        public static void InterceptWriteLine(string? message)
        {
            Console.WriteLine($"INTERCEPTED! Original message was '{message}'");
        }
    }
}

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
    file sealed class InterceptsLocationAttribute(string filePath, int line, int column) : Attribute;
}
```

当然ながら、2 つのメソッドは互いのシグネチャを一致させる必要があります。インターセプターは、インターセプションの正確な位置、つまり絶対物理ファイル パスとファイル内の行と列の位置の両方に依存します。上記の例が機能しない場合は、`filePath`、`line`、または`column`引数を更新する必要があることがほとんどです。

また、`InterceptsLocationAttribute`は（現在）どのBCLアセンブリにも含まれていないことに注意が必要です。そのため、ソースジェネレーターは自分でその型を定義する必要があります。複数のソースジェネレーターがこの属性を利用する可能性があるため、重複を避けるために型をファイルスコープで定義することを検討すべきです。

では、理論の授業はこれで終了です — さあ、ReSharper と Rider を見てみましょう！

## 視覚的な手掛かりとナビゲーション
インターセプターの中心的な問題は、実際にどのコードが実行されているかについて誤解を招く可能性があることです。ソースコードを見ただけでは、呼び出しがインターセプトされているかどうかを確信することはできません。古典的な「定義に移動」ナビゲーションさえも、間違った方向に導く可能性があります。そして、元のメソッドとインターセプトされたメソッドの挙動が異なり始めると、その危険が顕在化し、実際の「ハイゼンバグ」に直面することになります！

ReSharper と Rider は、インターセプトされた呼び出しの視覚的な手がかりとして、ガターアイコンやインレイヒントを導入しています。
![dotnet_howtos_20240117.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936166/d2ff36d5-ddf1-9671-0534-5ec554e85c7f.png "ガターアイコンとインレイヒントアイコン付きのインターセプトされた呼び出し")

ガターアイコンをクリックするか、インレイヒントをコントロールクリックすることで、インターセプトするメソッドに移動できます。インターセプト側でも同じ視覚的な手がかりが表示されるため、両方のメソッド間を行き来することができます。
![dotnet_howtos_20240117-2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936166/2295b7c4-da17-c969-cd13-5ac219b829ec.gif "インターセプトされたメソッドとインターセプトするメソッド間のナビゲーション")

インターセプターヒントの設定についてすべてをご紹介するために、手作りの例を離れ、`dotnet new webapiaot` テンプレートに含まれる適切なソースジェネレーターを使用する必要があります。

## インレイヒントの設定
インターセプターヒントは、Alt-Enter メニューからまたはインレイヒント自体を右クリックすることで設定できます。ここから、表示モードの一つ（プッシュ・トゥ・ヒントを含む）を選択するか、ヒントを完全に無効にすることができます。
![](https://storage.googleapis.com/zenn-user-upload/e4b82f6b9a88-20241120.gif "インレイヒントの表示設定")

インレイヒントアイコンは比較的小さいため、デフォルトの表示モードは「常に表示」に設定されています。デフォルトを変更したり、設定から構成済みのソースジェネレーターのリストを変更したりすることができます。設定は「エディター | インレイヒント | C# | インターセプターヒント」から行えます。
![](https://storage.googleapis.com/zenn-user-upload/d8bd66d9b055-20241120.png "インターセプターのヒントのオプションページ")

## 結論
この記事では、インターセプターを有望なプレビューフィーチャーとして、またソースジェネレーターの拡張として紹介しました。今すぐReSharper 2023.3またはRider 2023.3を試して、インターセプションを見逃すことなく体験してください！追加のサポートが必要な場合は、ぜひ下記のコメントでお知らせください。いつもご覧いただき、ありがとうございます。