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

Chapter6 ラムダ式(前編)

川俣 晶
2009/10/19

6.8 ラムダ式で継承を置き換えてみる

 ラムダ式の特徴を実感するために、継承によって書かれたコードを、ラムダ式を使って書き換えて比較してみよう。

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

 まずはコードを比較して眺めてみよう(リスト6.13リスト6.14参照)。

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です。
   // 私が朝丘夏美です。
  }
}
リスト6.13 抽象クラスバージョン

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(()=>
    {
      Console.WriteLine("私が{0}です。", name);
    });
  }

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

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

    Array.ForEach(persons, (p)=>
    {
      p.SayMyName();
    });
    // 出力:
    // 私がLです。
    // 私が朝丘夏美です。
  }
}
リスト6.14 ラムダ式バージョン

 まず、ラムダ式バージョンでクラスの数が2つ減ったことに注意を払おう。

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

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

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

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

  • ソースコードに書き込む文字数が減り、楽ができ、間違いが混入する可能性も減る
  • 名前でほかのメソッドからは参照できないので、誤ってほかの場所から参照するコードを書いてしまうリスクが減る

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

 さて、ラムダ式版(リスト6.14)はメリットが多いように見えるが、もちろん万能ではない。

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

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

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


 INDEX
  [完全版]究極のC#プログラミング
  Chapter6 ラムダ式(前編)
    1.6.1 おかずでもデザートでもなく“ご飯”
    2.6.2 ラムダ式とは何か?/【C#olumn】定義済みデリゲートを活用しよう
    3.6.3 ラムダ式は上位スコープにアクセスできる
    4.6.4 キャプチャされる変数
    5.6.5 注意を要するキャプチャの本質
    6.6.6 デリゲートの共変性と反変性
    7.6.7 デリゲートインスタンスの等価性
  8.6.8 ラムダ式で継承を置き換えてみる
    9.6.9 C# 2.0と匿名メソッド/【Exercise】練習問題
 
インデックス・ページヘ  「[完全版]究極の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 記事ランキング

本日 月間