partialコンストラクターの復習
前回、partialクラスのコンストラクターを扱いました。ここでは、そのpartialコンストラクターに至るまでの経緯として、自動生成側にコンストラクターを直接書いた場合に何が起きるのかを補足しておきます。
partialクラスとして2つのファイル(手書き側のForm1.csと、自動生成側のForm1.Designer.cs)に分かれている場合、コンストラクターをどちらか一方に書くこと自体はもともと可能でした。
自動生成側にコンストラクターを書くとどうなるか
まず、Form1.Designer.cs(自動生成側)を開いた状態です。この時点ではコンストラクターは定義されておらず、エラーもありません。
図1: Form1.Designer.cs(自動生成側)。まだコンストラクターは書かれていない状態
ここで、Form1.Designer.cs側に public Form1() { } というコンストラクターを書き始めます。しかしForm1.cs(手書き側)には、もともと public Form1() { InitializeComponent(); } というコンストラクターがすでに存在しているため、書いている途中からエラーが発生します。
図2: 自動生成側にコンストラクターを書き始めると、手書き側の定義と重複してエラーになる
これは、同じクラスの中に同じシグネチャのコンストラクターが2つ存在することになるため、「同じ定義がある」という趣旨のエラーになるためです。手書き側からすると、自分では何も変えていないのに急にエラーになるので、なぜかわからなくなってしまいます。
手書き側を消すと「幽霊コード」になる
このエラーを解消するには、手書き側(Form1.cs)のコンストラクターを書けなくする、つまり削除するかコメントアウトする必要があります。もともとForm1.csにあったコンストラクターは次のようなものでした。
図3: Form1.cs(手書き側)にもともと書かれていたコンストラクター
このコンストラクターをコメントアウトすると、自動生成側のコンストラクターだけが有効な定義として残り、エラーは消えます。
図4: 手書き側のコンストラクターをコメントアウトすると、エラーが解消される
この状態のForm1.csを見ると、コンストラクターの姿はコメントとしてしか残っていません。実際の処理はすべてForm1.Designer.cs側に書かれており、そちらだけを見ればアプリケーションは問題なく動作します。
図5: 自動生成側(Form1.Designer.cs)にInitializeComponent()の呼び出しまで書き終えた状態。処理の実体はここに隠れている
これがまさに「幽霊メソッド」「幽霊コード」と呼べる状態です。手書き側のファイルだけを見ている開発者からすると、コンストラクターがどこにあるのか、そもそも存在するのかどうかもわからないまま、実際には存在して動いている、ということになってしまいます。
つまり、今までも自動生成側にコンストラクターを書くこと自体ができなかったわけではありません。書いてしまうと手書き側に支障が出る(書けなくなる、または存在が見えなくなる)という問題があったのです。
partialで「契約による実装」にする
この問題を解消するのが、前回扱ったpartialコンストラクターです。Form1.cs(手書き側)に、実装を持たない次のような定義パーツを書きます。
図6: 手書き側にpartialコンストラクターの定義パーツを書く。エラーは発生しない
public partial Form1(); という宣言だけの一文(定義パーツ)を手書き側に書き、実装本体は自動生成側の public partial Form1() { InitializeComponent(); } に持たせます。こうすることで、手書き側は「このクラスにはコンストラクターが存在し、実装は別の場所(自動生成側)にある」ということを、コードの上で明示的に理解した状態になります。
これは、自動生成側が一方的にコンストラクターを使っているのではなく、手書き側もその存在を合意した上で成り立っている、という意味で「契約による実装」と言えます。partialで両方のファイルが同じメンバーの存在について合意していることが、コード上にはっきり表れるわけです。
コンストラクター自体は手書き側に書けない点への対策
ただし、この方法でもコンストラクター本体を手書き側に書くことはできません。定義パーツはあくまで宣言のみで、実装は自動生成側に置く必要があるからです。この制約への対策として、前回はInitのような通常の部分メソッド(partialメソッド)を別途用意し、コンストラクターの代わりにそちらを手書き側で使ってもらう、という流れで解決していました。
まとめると、自動生成側にコンストラクターを書くこと自体は以前から可能でしたが、書いてしまうと手書き側に支障が出て「幽霊コード」化してしまう問題がありました。partialコンストラクターにより、手書き側と自動生成側が合意(契約)した上でコンストラクターを分割して書けるようになり、この問題が解消された、ということになります。
Udemyでの動画版はこちらからご覧いただけます
00_はじめに
01_VisualStudio2026をインストールしてください
A01_fieldキーワード
B01_extensionブロックとは
B02_拡張メソッドとは
B03_extensionブロック
B04_型パラメータありの拡張メソッド
B05_型パラメータありのextensionブロックの書き方
B06_拡張プロパティ
B07_拡張静的プロパティ&メソッド
B08_拡張演算子
C01_partialコンストラクター
C02_幽霊コードを定義パーツで契約による実装をするという考え方
C03_partialイベント
D01_Null条件付き代入
E01_複合代入演算子のオーバーロード
F01_明示的な型を指定せずにラムダパラメータでパラメータ修飾子を使用する
G01_nameofはバインドされていないジェネリック型をサポートします
H01_SpanTおよびReadOnlySpanTのより暗黙的な変換
Udemyでの動画版はこちらからご覧いただけます