C# 9のソースジェネレータ(Source Generator)は、コンパイル時にコードを調査して追加的にソースコードを挿入するようなコンパイラ拡張を可能にする。挿入されたコードは、コンパイルされたものとまったく同じアセンブリに組み込まれる。この機能を活用するためにMicrosoftは、パーシャルメソッドの制限の大部分を撤廃することにしている。
パーシャルメソッド(Partial Method)は2007年に、ソースコードジェネレータの機能を拡張する目的でVBとC#に導入された。基本的な考え方は、ソースコードジェネレータがパーシャルメソッド(partial method)を起動するコードを出力する、というものだ。生成されるコードの動作を修正したい場合、開発者はパーシャルメソッドを実装することができる。それ以外の場合は、パーシャルメソッドは、そのメソッドの呼び出しとともに削除される。
ソースジェネレータでは、このストーリが逆転した。開発者が最初に、デザインするパーシャルメソッドを定義する。ソースジェネレータがそのパーシャルメソッドを確認すると、周辺情報と外部情報を組み合わせてそれを実装する。例えば、パーシャルメソッドの属性とデータベースのスキーマを組み合わせることで、データベースを呼び出す完全なSQLを持ったメソッドを生成することが可能になる。
現時点のパーシャルメソッドには、次のようないくつかの制限がある。
- パーシャルメソッド宣言の戻り値はvoidでなくてはならない
- パーシャルメソッドのパラメータはoutではなく、inまたはrefでなければならない
- パーシャルメソッドは暗黙的にprivateであり、従ってvirtualにはできない
最初のパーシャルメソッド拡張プロポーザルにより、これらの制限が撤廃されることになった。その理由について、プロポーザルでは次のように説明している。
これにより、パーシャルメソッドが参加可能なソースジェネレータのシナリオセットが拡張され、結果としてソースジェネレータの機能と適切にリンクするようになります。例えばregexは、次のようなパターンを使って定義できます。
[RegexGenerated("(dog|cat|fish)")] partial bool IsPetMatch(string input);
このように、ジェネレータにオプトインする宣言方法がシンプルになると同時に、ジェネレータがソースコードを探索してコードを生成するための宣言セットも非常に簡単なものになるのです。
この結果、削除不可能だが実行できることに制限がないという、パーシャルメソッドの第2のカテゴリが生まれることになる。2つのカテゴリを区別するための、基本的なルールが2つある。
- パーシャルが明示的な修飾子(public、privateなど)を伴う場合、必ず実装しなければならないが、返り値はvoid、パラメータはout以外という制限はない。
- パーシャルに明示的な修飾子のない場合、削除が可能だが、返り値はvoid、パラメータはout以外という制限がある。
純粋に技術的な観点から見れば、この新しいカテゴリのパーシャルメソッドは、必ず実装しなければならないという点で、実際には抽象(abstract)メソッドである。しかし必ずしもvirtualではないことから、Microsoftは、abstractというキーワードの転用が混乱を招くと判断したのだ。
パーシャルプロパティ、パーシャルコンストラクタ、パーシャル演算子など他クラスのメンバについては、C# 10に向けて検討されている。ただしこれらは、ソースジェネレータの機能拡張に必須なものではないため、必要以上に複雑な機能(feature creep)の導入は望まれていないのが現状である。