連載:C# 2.0入門

第5回 匿名メソッドとデリゲート

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

おかずでもデザートでもなくご飯

 この連載の構成を考えたとき、匿名メソッドはC# 1.0から見れば飛躍が大きいので、後に回した方がよいと考えた。しかし、実際にはこれまでの連載の中のサンプル・コードで、すでにしばしば匿名メソッドを使用している。

 それが何を意味しているのか、食事に例えてみよう。

 典型的な食事は、ご飯、おかず、デザートなどで構成されている。ご飯はいつも同じようなものが出てくるが、おかずは毎回変わることが多い。そして、デザートは食事のメインではないが、特に目立つ花形である。

 この中で、int型のような基本的なデータ型はもちろんご飯に当たる。どのようなプログラムでも変わりなく使われ、しかも使用量がいつも多いからだ。

 一方、ASP.NET用のクラスなどは、おかずに当たるだろう。それは作るプログラム次第ではまったく使われないこともあるが、逆に非常に「おいしく」活用できることもあるからだ。またWMIを扱うクラスなどは、デザートに当たるだろう。システムの管理処理にしか使われない、まさに特別に目立つ存在だからだ。

 では、匿名メソッドはご飯、おかず、デザートのどれに当たるだろうか。

 特殊なときに活用する機能というイメージを持っていれば、おかずやデザートだと思うかもしれない。しかし、実際はご飯に当たるのだ。C# 2.0に慣れれば慣れるほど、匿名メソッドの使用量は増えていくように思う。しかも、ジャンルを問わず、どのようなプログラムを書いている場合にも増えていくのだ。

 実はこの原稿を書きながら、驚くほどC# 2.0プログラミングでストレスを感じないことに気付いた。これまで使ってきたほかのプログラム言語では大なり小なり感じてきた、さまざまなストレス要因がほとんど存在しないのである。それは、書きたいことと、書かれたコードのギャップが極めて少ないことを意味する。

 例えばC# 2.0では、ソース・コードのあちこちを移動しながら定義を書き込まねばならないような、回りくどいことはあまり経験しない。C++ならヘッダファイル(.h)とソース・コード本体(.cpp)の定義を一致させるであるとか、ほかのOOP(Object Oriented Programming:オブジェクト指向プログラミング)言語ならインターフェイスの定義と実装の定義を整合させるであるとか、そういった離れた場所の定義を整合させる作業が減ったような気がする。それらは、書きたいことと実際のコードの間に入り込んでくる不純物のような存在だったのかもしれない。

 逆に、匿名メソッドは、そういった不純物を取り除く有力な手段となっているように感じられる。2つのメソッドが関連して動作するようなとき、どうしてもソース・コードの2つの個所を往復しながらコードを書く形になる。しかし、メソッドの中に匿名メソッドを書き込むことができれば、ソース・コードの往復が減るのである。往復が減れば楽ができるので、匿名メソッド使用へのストレス感もなくなる。ストレス感がなくなれば、もっと多用する。その結果として、ソース・コードはより小さなメソッドに分割され、分かりやすさやメンテナンス性も上がるというわけである。

 このようにして使われる匿名メソッドは、まさにおかずでもデザートでもなくご飯に当たる存在としてコーディングを支えてくれる。

匿名メソッドとは何か?

 すでに連載の中で何回も使っているので軽く流そう。

 匿名メソッドとは名前のないメソッドであり、生成結果(=匿名メソッドの記述内容)はそのままデリゲート型の変数や引数に入れて使う。つまり呼び出しは、名前ではなく、デリゲート型の変数や引数などを経由して行う。

 匿名メソッドの作成は特に難しいことはなく、delegateキーワードの後にメソッドと同様に引数リストを書き、その後にメソッド本体を書くだけである。

using System;

delegate void MyAction(string message);

class Program
{
  static void Main(string[] args)
  {
    MyAction action = delegate(string message)
    {
      Console.WriteLine(message); // 匿名メソッドの内容
    };
    action("Hello! World!"); // 出力:Hello! World!
  }
}
リスト1 匿名メソッドの記述例

 この例であれば、匿名メソッドはデリゲートであるMyAction型の変数actionを経由して呼び出しを行っている。呼び出しそのものは、C# 1.0時代のデリゲート経由のメソッド呼び出しと何ら変わりはない。

 このプログラムを、匿名メソッドを使わないで書き直すと以下のようになる。

using System;

delegate void MyAction(string message);

class Program
{
  static void SayMessage(string message)
  {
    Console.WriteLine(message);
  }

  static void Main(string[] args)
  {
    MyAction action = SayMessage;
    action("Hello! World!"); // 出力:Hello! World!
  }
}
リスト2 名前のあるメソッドでリスト1を書き直した例

 匿名メソッドを使うと、“名前のあるメソッド”の中に匿名メソッドが埋め込まれる形になり、関連するコードがひとまとまりになる。しかし、匿名メソッドを使わない場合は2つのメソッドが分離されてバラバラになっている(リスト2の場合ではSayMessageメソッドとMainメソッド)。メソッドの数が多いクラスであれば、この2つのメソッドははるか遠くに分かれて記述されるかもしれないが、それを関連付けて読むのは骨が折れる。

[コラム]定義済みデリゲートを活用しよう

 匿名メソッドを使う場合には、必然的にデリゲート型として定義された型を必要とする。その点で、名前のあるメソッドよりも手間が増えるような印象があるかもしれない。しかし、クラス・ライブラリには、MethodInvoker、Action、Predicateなど、汎用的に使用できる便利なデリゲート型が定義されている。それらを活用して済むことも多いだろう。

 例えば、次のリスト3はActionデリゲート(System名前空間)を使うことで、デリゲート型の宣言をソース・コードから排除できる。

using System;

class Program
{
  static void Main(string[] args)
  {
    Action<string> action = delegate(string message)
    {
      Console.WriteLine(message);
    };
    action("Hello! World!"); // 出力:Hello! World!
  }
}
リスト3 Actionを使ってdelegate型宣言を排除した例

 Action<string>デリゲートは、文字列を引数に取るvoid型のメソッドに対して使用できる、クラス・ライブラリで定義済みのデリゲート型である。



 INDEX
  C# 2.0入門
  第5回 匿名メソッドとデリゲート
  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 記事ランキング

本日 月間