連載:[完全版]究極のC#プログラミング

Chapter19 小さな改善とコンパイラの新機能

川俣 晶
2010/05/24
Page1 Page2 Page3 Page4

19.2 部分メソッド定義

 「部分メソッド定義」(Partial Method)は、確かにC# 3.0の新機能だが、C# 3.0関連資料から漏れていることも多く、見落とされがちな機能といえる。また、単なる筆者の見落としかもしれないが、有効活用されている事例も見た記憶がない。

 この部分メソッド定義とは、部分クラス定義(Partial Class)とともに使用する機能で、部分クラス定義が複数あるとき、その中の1つでメソッドの名前と引数だけを定義し、実体は別の部分クラス定義に記述する機能である。

 具体的な内容は、次のリスト19.2のサンプルコードを見たほうがわかりやすいだろう。

using System;

partial class A
{
  partial void sample()
  {
    Console.WriteLine("Sample called");
  }
}

partial class A
{
  partial void sample();

  public void CallSample()
  {
    sample();
  }
}

class Program
{
  static void Main(string[] args)
  {
    var a = new A();
    a.CallSample();
    // 出力:Sample called
  }
}
リスト19.2 部分メソッド定義の記述例

 もちろん、実際に使用する場合は、個々の「partial class A」の定義は別ファイル上にあって分離されていることが多いだろう。

 さて、部分メソッド定義は「partial void sample();」のようにpartialキーワードを付けて指定する。このメソッドは2つ目の「partial class A」で宣言されているが、実装本体は1つ目の「partial class A」にある。

 この機能のポイントは、実装がないときはエラーも出さずに呼び出しごと消えてなくなることにある。そう、実体がなくなるだけではなく、実体への呼び出しごと消えてなくなるのである。

 具体的に見るために、.NET Reflector(ILDSAMでもOK)でCallSampleメソッドを逆アセンブルしてみよう。

.method public hidebysig instance void CallSample() cil managed
{
  .maxstack 8
  L_0000: nop
  L_0001: ldarg.0
  L_0002: call instance void A::sample()
  L_0007: nop
  L_0008: ret
}

 確かに、sampleメソッドをcallするCILの命令が含まれている。では次のように、1つ目のsampleメソッドをコメントアウトして同じことを試してみよう。

//partial void sample()
//{
//  Console.WriteLine("Sample called");
//}

 すると、次のようになる。

.method public hidebysig instance void CallSample() cil managed
{
  .maxstack 8
  L_0000: nop
  L_0001: ret
}

 見てのとおり、sampleメソッドをcallするCILの命令は消えてなくなった。

 このような働きから、部分メソッド定義は拡張可能な部分クラスに利用できることがわかる。たとえば、実行中に発生するさまざまな出来事を部分メソッドの呼び出しで伝達するように部分クラスを記述できる。このようなイベントは、別の部分クラスに部分メソッドの実体を定義することで受け取って処理できる。

 この仕掛けは、C#自体が持つEventを使えば同等の機能が記述できるように思えるかもしれない。だが、部分メソッド定義を使って実現した場合、1つだけ決定的な長所がある。それは、処理する実体がないイベントは、イベントの呼び出しコードそのものが消滅することである。それにより、従来のEventよりも、よりコンパクトで効率の良いコードが生成可能となる。

 しかし、Eventと違って部分メソッド定義は呼び出し先の確定作業をコンパイル時に行う必要があるため、自由度は低い。実行時に必要に応じてイベントハンドラを付けたりはずしたりはできないのである。さらに部分メソッド定義は次のような制約を持つ。

  • void型を返す必要がある
  • outパラメータは使用できない
  • 暗黙的にprivateとなる。リスト19.2の「partial void sample();」は、privateとは明記していないがprivateとして扱われているので、外部から直接呼び出すことはできない
  • virtualにはできない(privateである以上、virtualにする意味もない)
  • externにはできない
  • 部分メソッドに対するデリゲートを作ることはできない

 ただし、次のことは可能だ。

  • refパラメータは使用できる
  • static修飾子とunsafe修飾子は使用できる
  • ジェネリックメソッドにできる(第2章2.9節で解説した制約は部分メソッドの定義宣言に置き、必要に応じて実装宣言で繰り返すことができる。パラメータ名と型パラメータ名は、定義宣言と実装宣言で同じである必要はない)

 これらの制約を見てわかるとおり、制限は厳しい。おそらく、開発中のプログラムに後から部分メソッド定義の機能を当てはめてスピードアップを図るような使い方はうまくいかないことも多いだろう。設計段階から、部分メソッド定義が適用できるように配慮しないとなかなかうまく機能しないかもしれない。しかし、性能面が問題になる場合は使うと価値があるかもしれない。


 INDEX
  [完全版]究極のC#プログラミング
  Chapter19 小さな改善とコンパイラの新機能
    1.19.1 インライン警告制御「#pragma warning」
  2.19.2 部分メソッド定義
    3.19.3 固定サイズバッファ
    4.19.4 volatileがIntPtr型およびUIntPtr型へ適用できる/練習問題
 
インデックス・ページヘ  「[完全版]究極のC#プログラミング」


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 記事ランキング

本日 月間