連載:C# 2.0入門

第6回 部分クラスと静的クラス

株式会社ピーデー 川俣 晶
2007/10/30
Page1 Page2 Page3 Page4

部分クラス(Partial Class)

 以前、C# 1.xの入門連載を書いていたとき、eventの機能を解説する際にGUIのイベント処理を例題に取り上げないで、コンソール・アプリケーションを使っているのはおかしいという読者の感想があった。eventとはマウスをクリックしたなどのイベント処理を行うための機能であり、それを例題として取り上げないのは直感的に分かりにくいという意見だろう。

 しかし、そのような解釈は適切ではない。なぜかといえば、GUIのイベント処理を例題として使うことには1つの明確なデメリットがあり、コンソール・アプリケーションを例題に使うことには2つの明確なメリットがあると判断したから。

 具体的なデメリットは以下のようなものだ。

  • eventはGUIのイベント処理以外でも使われるにもかかわらず、それを行うための機能という先入観を与えかねない

 そして、メリットは以下のとおりである。

  • eventはGUIのイベント処理のための機能という先入観を与えないどころか、むしろ明確に否定する
  • GUIのソース・コードは本質的にeventとは関係のないコードが多く入り込み、サンプル・コードが分かりにくくなるが、コンソール・アプリケーションはそのようなコードが少ない

 さて、今回扱う「部分クラス」の事情もこれに似ている。部分クラスが最も的確に利用される分かりやすい事例は、Windowsフォームの自動生成コードの分離など、GUIにかかわるプログラムだろう。しかし、それは部分クラスの1つの使い方でしかなく、そのための機能という先入観を持つことは好ましくはない。そこで、ここでもコンソール・アプリケーションを使ってサンプルを見ていくことにしよう。

 部分クラスが持つ機能はそれほど難しいものではない。以下のひと言で9割の説明は終わってしまう。

  • クラス、構造体、インターフェイスの宣言を複数の固まりに分割できる(ただし、1回のコンパイルで同時に処理される範囲内で)

 単に分割できるだけでは、そのありがたみがあまり分からないだろう。分割の結果、発生する具体的なメリットは以下である。

  • 宣言を複数のソース・ファイルに分割して書き込める

 それによってもたらされる具体的な効能は以下のようなものだ。

  • 自動生成されるコードとプログラマーが記述するコードをスマートに混在できる
  • 巨大なクラスを複数のソース・ファイルに分割して記述できる

 このような効能に対する疑問はあると思うが、それは後に取り上げるとして、まずは具体的な使用例を見てみよう。

 リスト2〜3は、SampleClassという名前のクラスを、部分クラスの機能を使って2つのソース・ファイル(Program.csとp1.cs)に分けて記述した例である。

using System;

partial class SampleClass // 部分クラス(SampleClassの一部)
{
  public static void SayHello()
  {
    say("Hello!");
  }
}

class Program
{
  static void Main(string[] args)
  {
    SampleClass.SayHello(); // 出力:Hello!
  }
}
リスト2 Program.cs

using System;

partial class SampleClass // 部分クラス(SampleClassの一部)
{
  private static void say(string message)
  {
    Console.WriteLine(message);
  }
}
リスト3 p1.cs

 これを見ると分かるとおり、SampleClassというクラスの宣言がどちらにもあり、それぞれclassキーワードの手前に「partial」というキーワードが書かれている。ちなみに、partialは予約語ではないので、ほかの個所でこの名前を変数などで使っても問題はない。

 さて、ここで注目すべき点は、privateとして宣言したsayメソッドを、別のソース・ファイル上のSayHelloメソッドから呼び出している点である。このような呼び出しは、C# 1.xではできないものだった(delegate型の値としてメソッドを受け渡せば呼び出せたが、直接は呼び出せない)。

 しかし、これらのサンプル・コードはコンパイルでき、実行できる。なぜなら、一見2つに見えるリスト2とリスト3のクラスの宣言は、実はpartialキーワードの効能により、1つのクラス宣言として扱われるからである。

 それが意味するところは、コンパイルにより作成した実行ファイルを.NET Reflectorで逆コンパイルして調べると明瞭に分かる。

internal class SampleClass
{
  // Methods
  private static void say(string message)
  {
    Console.WriteLine(message);
  }

  public static void SayHello()
  {
    say("Hello!");
  }
}
リスト4 .NET Reflectorで逆コンパイルしたSampleClassクラス

 つまり、SampleClassはコード上で2つに分かれて宣言されているだけで、内部的には「たった1つのクラス」として生成されているわけである。

自動生成コードと安全に共存する

 部分クラスの実例をすぐ見たければ、Visual Studio 2005でWindowsアプリケーションやASP.NETアプリケーションのプロジェクトを作成するとよいだろう。

 Windowsアプリケーションであれば以下のようなコードが最初から生成されている。

public partial class Form1 : Form
{
  ……

 ASP.NETアプリケーションであれば以下のようなコードが最初から生成されている。

public partial class _Default : System.Web.UI.Page
{
  ……

 これらはpartialというキーワードがあることから分かるとおり、部分クラスである。これらのクラスは、プログラマーが自分で書く部分と、ツールが自動生成するコードを明確に分割するために使われている。


ソリューション・エクスプローラで表示される自動生成されたコード

 このうち、部分クラスの状況を見やすいのはWindowsアプリケーションだろう。普通にコードを開くとプログラマーが自分で記述するコードが開くが、ソリューション・エクスプローラで「フォーム名.Desinger.cs」のファイルを開くと、自動生成される側のコードを見ることができる。

 例えば、フォームをダブルクリックしてLoadイベントのハンドラを生成させた場合、Form1.Designer.cs側のInitializeComponentメソッドには以下の行が追加される。

this.Load += new System.EventHandler(this.Form1_Load);
Form1.Designer.csのInitializeComponentメソッドに追加されるコード

 そして、Form1.cs側には以下のメソッドが追加される。

private void Form1_Load(object sender, EventArgs e)
{

}
Form1.csに追加されるコード

 このとおり、追加されたメソッドForm1_Loadはprivateではあるが、同じクラス内なので「new System.EventHandler(this.Form1_Load)」という形で参照することができる。

 さて、ここでソース・ファイルが2つに分割されている効能について考えてみよう。先ほど、部分クラスは、自動生成されるコードとプログラマーが書くコードが混在したクラスをスマートに実現するために使用されると書いた。しかし、Form1_Loadメソッドも自動生成されている以上、このケースでは「どちらのソース・ファイルも自動生成の対象になっている」のである。

 だが、その後のコードの管理という観点から見ると、両者には決定的な差があることが分かる。

this.Load += new System.EventHandler(this.Form1_Load);

という1行は、フォーム・デザイナのプロパティ管理機能(その内容はVisual Studio 2005のプロパティ・ウィンドウに表示されている)が持つイベントの管理機能を使って、完全に「ソース・コードを一切見ることなく」マウスの操作だけで管理ができるようになっている。つまり、全自動で扱うことができるコードなのである。

 一方、Form1_Loadメソッドの方は、何の自動管理も用意されていない。プログラマーが自分で記述し、健全性を維持し続けねばならない。例えば、フォームの名前をForm1から別の名前に変更しても、Form1_Loadというメソッド名が自動的に変わるわけではない。ソース・コードを扱う主導権は100%プログラマーに与えられている。

 つまり、以下の2つのソース・ファイルには、異なる役割が与えられているのである。

Form1.cs
  サービスで空のメソッドを自動挿入することもあるが、基本的にプログラマーが自分の判断ですべて自由に書き換えてよい部分。

Form1.Designer.cs
  プログラマーに一切コードを見せることなく、フォーム・デザイナがすべて適切に書き換えを行う部分。プログラマーが直接書き換えると、正しく管理できなくなる可能性があって危険。

 ここでポイントになるのは、「プログラマーが直接書き換えると、正しく管理できなくなる可能性があって危険」という部分である。そのようなコードは別ファイルに分けてしまい、書き換えられないようにすると安全性が高まる。また、プログラマーが書き換えてよい部分だけを見ながら作業できれば、作業効率もアップする。

 部分クラスとは、そのような効能を得るために使用されている。

 さて、ここで同様の効能を得るために、部分クラスのような新しい機能を導入せずとも、継承などの既存の機能を使ってもよいのではないか……と思った読者もいると思う。しかし、本来1つのクラスで済むものを、コード編集の都合だけで2つに分けて継承関係を持たせることは別の意味での過剰な複雑さを持ち込むことになってしまう。とかく継承は問題を起こしやすいので、継承が本当に必要でない場面では、使わない方が安全性だろう。そういう意味で、部分クラスは継承のように過剰に強力すぎることはなく、リーズナブルな機能といえる。


 INDEX
  C# 2.0入門
  第6回 部分クラスと静的クラス
    1.値型と参照型の相違は何か?
  2.部分クラス/自動生成コードと安全に共存する
    3.リフレクションと部分クラス/部分クラスの注意点
    4.静的クラス/アクセサのアクセシビリティ/アクセシビリティ指定の制約
 
インデックス・ページヘ  「C# 2.0入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間