連載:C# 2.0入門

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

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

匿名メソッドで継承を置き換えてみる

 匿名メソッドの特徴を実感するために、継承によって書かれたコードを、匿名メソッドを使って書き換えて比較してみよう。

 ここでは、自分の名前を出力する機能を持った人物(Person)クラスを使う。その際、名前がchar型で与えられるケースと、string型で与えられるケースがあり、それらを継承によって使い分けているとしよう。ちなみに、これだけならPersonクラスはインターフェイスでも構わないし、ほかにももっと効率的に書ける個所はあるが、より大きく複雑なコードの一部を切り出して単純化したという想定なので、このままいこう。

 まずはコードを比較して眺めてみよう。

using System;

abstract class Person
{
  public abstract void SayMyName();
}

class StringNamePerson : Person
{
  private string name;

  public override void SayMyName()
  {
    Console.WriteLine("私が{0}です。", name);
  }

  public StringNamePerson(string name)
  {
    this.name = name;
  }
}

class CharNamePerson : Person
{
  private char name;

  public override void SayMyName()
  {
    Console.WriteLine("私が{0}です。", name);
  }

  public CharNamePerson(char name)
  {
    this.name = name;
  }
}

class Program
{
  static void Main(string[] args)
  {
    Person[] persons = {
      new CharNamePerson('L'),
      new StringNamePerson("朝丘夏美"),
    };

    Array.ForEach(persons, delegate(Person p)
    {
      p.SayMyName();
    });
    // 出力:
    // 私がLです。
    // 私が朝丘夏美です。
  }
}
リスト14 抽象クラス・バージョン

using System;

delegate void MyMethodInvoker();

class Person
{
  public readonly MyMethodInvoker SayMyName;

  public Person(MyMethodInvoker sayMyName)
  {
    SayMyName = sayMyName;
  }

  public static Person CreateStringNamePerson(string name)
  {
    return new Person(delegate()
    {
      Console.WriteLine("私が{0}です。", name);
    });
  }

  public static Person CreateCharNamePerson(char name)
  {
    return new Person(delegate()
    {
      Console.WriteLine("私が{0}です。", name);
    });
  }
}

class Program
{
  static void Main(string[] args)
  {
    Person[] persons = {
      Person.CreateCharNamePerson('L'),
      Person.CreateStringNamePerson("朝丘夏美"),
    };

    Array.ForEach(persons, delegate(Person p)
    {
      p.SayMyName();
    });
    // 出力:
    // 私がLです。
    // 私が朝丘夏美です。
  }
}
リスト15 匿名メソッド・バージョン

 まず、匿名メソッド・バージョンでクラスの数が2つ減ったことに注意を払おう。

 継承を取りやめたことで、継承によって導出された2つのクラスStringNamePerson、CharNamePersonは消滅した。クラスPersonからabstractキーワードが一切消滅したのも、継承を取りやめた反映である。

 また、抽象メソッドSayMyNameは、デリゲート型でreadonlyの変数の宣言に置き換えられた。抽象メソッドとデリゲート型の変数は、実は機能性が似通っているので、置き換えることが可能となるケースがある。しかし、完全に同じではないので、置き換えられないこともある。この問題は後で説明する。

 さて、次に注目すべき点は、消えた2つのクラスのコードがどこへ行ったのか……である。その答えは、Personクラス内のCreateStringNamePersonとCreateCharNamePersonというメソッドである。この2つは、オブジェクトのインスタンスをnewする機能に相当する機能を発揮するために用意されたメソッドである。それぞれ、引数に名前を出力するための匿名メソッドを渡してPersonクラスをnewしている。

 ここで注意を払う価値があるのは、名前の情報を保存するためにフィールドを宣言する必要が消滅していることである。これらのメソッドでは、名前を受け取る引数が匿名メソッドによってキャプチャされることにより、明示的な保存場所を宣言することなく、必要なだけ名前の情報を保存し、それを出力することができる。この特徴は以下の2つのメリットを生む。

  • ソース・コードに書き込む文字数が減り、楽ができ、間違いが混入する可能性も減る

  • 名前でほかのメソッドからは参照できないので、誤ってほかの場所から参照するコードを書いてしまうリスクが減る

 2つ目は、ほかのメソッドから参照したいときにできないことを意味しており、メリットとはいい切れない……という意見もあると思うが、そのメソッドも匿名メソッドとして同じメソッド内に書いてしまえば参照できるのでさほど大きな問題ではない。

 さて、匿名メソッド版(リスト15)はメリットが多いように見えるが、もちろん万能ではない。

 例えば、クラスに属するメソッドは、自分自身のオブジェクトを参照するthisキーワードを使うことができるが、匿名メソッドではできないのである。もちろん、匿名メソッドでもthisを使うことはできるが、そのthisが参照するのは匿名メソッドを作成したメソッドが持つthisであって、別のものである。

 しかし、thisが使えないといっても、上記のサンプル・コード(リスト15)のように、キャプチャを活用することで実質的に問題なく使える例もある。

 以上のように、匿名メソッドは継承の機能を置き換えられるケースも多く、その際はコードの簡素化などのメリットが得られることも多い。しかし、すべての継承を置き換えられるわけではなく、依然として継承機能が必要とされるケースは残る。両者はうまく適材適所で使い分けていくとよいだろう。

次回予告

 次回は、部分クラスや静的クラスなど、クラスの強化について解説する予定である。Visual Studioでソース・コードを見ていて、あるはずの定義が見当たらない秘密はこの部分クラスにある。次回はそのナゾも解き明かしていこう。

 また、実はクールな可能性を秘めた静的クラスについても解説を行う予定である。End of Article


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

本日 月間