keygen — C#で拡張データを簡潔に結びつけるソースジェネレータ
概要
keygenは、C#のソースジェネレータとして動作し、ConditionalWeakTable(CWT)を用いてオブジェクトに拡張データを関連付けるためのコードを自動生成します。生成されるコードはC# 14の拡張メンバーを使って呼び出せるように設計されており、既存の型定義を変更することなく、インスタンスに付随する追加情報(メタデータや一時的な状態など)を安全に保持できます。ソースジェネレータは開発時のみに実行され、生成コードは通常のプロジェクトコードとして消費されるため、実行時のランタイム依存は発生しません。
リポジトリの統計情報
- スター数: 1
- フォーク数: 0
- ウォッチャー数: 1
- コミット数: 2
- ファイル数: 3
- メインの言語: 未指定
主な特徴
- C# 14の拡張メンバーを用いるソースコード生成で、既存型を改変せずに拡張データを付与できる。
- ConditionalWeakTableを利用して、ガベージコレクションに連動する弱参照ベースの関連付けを実現。
- コンパイル時にコードを生成する「dev-time only」設計で、ランタイム依存が残らない。
- 小さなプロジェクト構成で導入が容易(READMEとsrcのみ)。
技術的なポイント
keygenのコアは「ソースジェネレーション」と「ConditionalWeakTableによるデータ結びつけ」という二つの技術的柱で成り立っています。ソースジェネレータはRoslynの仕組みを利用して、開発時に対象となる型情報を解析し、必要な拡張メンバー(プロパティやメソッド)を自動生成します。生成されるコードは、各対象型ごとに内部的にConditionalWeakTable<TObject, THolder>のような構造を用意し、インスタンスをキー、拡張データを値として格納します。ConditionalWeakTableを使う利点は、参照のみで強参照を保たないため、オブジェクトがGCで回収される際に関連する拡張データも自動的に解放され、メモリリークのリスクが低減される点です。
また、C# 14の拡張メンバー(extension members)を前提に生成されるため、呼び出し側はまるで元の型に新しいメンバーが追加されたかのように自然に利用できます。これによりAPIの可読性と使い勝手が向上します。一方で、C# 14未対応環境では生成コードの想定する構文・機能が使えないため、導入前にコンパイラ互換性を確認する必要があります。
設計上の注意点としては、値型への関連付けやスレッド安全性、初期化タイミングの扱いがあります。ConditionalWeakTable自体はスレッドセーフな実装ですが、格納するホルダーオブジェクトの初期化ロジックや複数スレッドからの同時アクセスをどう扱うかは生成コードのポリシー次第です。また、シリアライズ/デシリアライズやリフレクションでの挙動も考慮する必要があります。keygenはあくまで「開発時にコードを生成する」ツールであり、実行時に新しい実装を注入するのではなく、生成後のコードが通常のソースとして管理される点も特徴です。
プロジェクトの構成
主要なファイルとディレクトリ:
- .gitignore: file
- README.md: file
- src: dir
まとめ
コンパイル時に拡張メンバーを自動生成し、CWTで安全にデータを結びつける実用的なソリューションです(導入はコンパイラ互換性要確認)。
リポジトリ情報:
- 名前: keygen
- 説明: keygen is a source generator for automatically relating objects to extended data through ConditionalWeakTables using C# 14 extension members
- スター数: 1
- 言語: null
- URL: https://github.com/gold-meridian/keygen
- オーナー: gold-meridian
- アバター: https://avatars.githubusercontent.com/u/220157870?v=4
READMEの抜粋:
keygen
keygen is a source generator for automatically relating objects to extended data through ConditionalWeakTables using C# 14 extension members.
It is a purely compile-time (dev-time) dependency and only serves to produce workable code that may be trivially consumed within your codebase and by other API consumers.
example
Given the class:
public sealed class MyClass
{
public int SomeData { get; }
}
You may want to relate additional information to an instan…