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

Chapter1 C# 3.0らしいプログラミングとは?

川俣 晶
2009/07/31

1.4 インターフェースとの比較

 ここまで見てきた2つの事例には、ラムダ式を使わずとも、継承、インターフェース、ポリモーフィズムなどを使ってきれいに実現できる……という反論がありうるだろう。

 これに答えるために、インターフェースとラムダ式を比較するサンプルを紹介しよう。

 内容は簡単で、名前と年齢のペアを含むオブジェクトの配列を年齢順にソートするというだけのものである。ただし、コマンドラインに引数を与えられた場合は逆順にソートするとしよう。

 リスト1.4は、ラムダ式のほかに「ジェネリック」(ジェネリックについては第2章で詳しく解説する)を使った例である。リスト1.5は、インターフェースを使い、できるだけ同等になるようVisual Studio .NET 2003(C# 1.x)で作成したものである。

using System;

class Person
{
  public readonly string Name;
  public readonly int Age;
  public Person(string name, int age)
  {
    Name = name;
    Age = age;
  }
}

class Program
{
  static void Main(string[] args)
  {
    Person[] persons = {
      new Person("加藤三郎", 36),
      new Person("浪速十三", 13),
      new Person("山本五十六", 56),
    };

    // このSortメソッドはジェネリックメソッドであり、
    // 任意の型の配列をソートできる
    Array.Sort(persons,
        (x, y) => (x.Age - y.Age) * (args.Length > 0 ? -1 : 1));

    foreach (Person person in persons)
    {
      Console.WriteLine("{0},{1}", person.Name, person.Age);
    }
  }
}
Pリスト1.4 ラムダ式とジェネリックを使ったソート(C# 3.0)

using System;
using System.Collections;

class Person
{
  public readonly string Name;
  public readonly int Age;
  public Person(string name, int age)
  {
    Name = name;
    Age = age;
  }
}

class PersonComparer: IComparer
{
  private int sign;

  int IComparer.Compare (object x, object y )
  {
    // Person型へのキャストが必要
    return (((Person)x).Age - ((Person)y).Age) * sign;
  }

  public PersonComparer(bool isReverse )
  {
    sign = isReverse ? -1 : 1;
  }
}

class ClassMain
{
  [STAThread]
  static void Main(string[] args)
  {
    Person[] persons = {
      new Person("加藤三郎", 36),
      new Person("浪速十三", 13),
      new Person("山本五十六", 56),
    };

    Array.Sort(persons, new PersonComparer(args.Length > 0));

    foreach (Person person in persons)
    {
      Console.WriteLine("{0},{1}", person.Name, person.Age);
    }
  }
}
リスト1.5 C# 1.xによるソート

 リスト1.4では、ソート条件の処理をラムダ式1つで処理している。その内容は、実質的に式1つである。内容が式だけである場合、ラムダ式は中カッコ({})とreturnを書かなくてよい。「()=>{return x;}」と「()=>x;」は等価なのである(詳しくは第7章で「式形式のラムダ」として解説)。この式には、コマンドライン引数の有無を調べる部分が含まれるが、もちろんラムダ式は上位のメソッドのスコープに含まれるので、上位メソッドの引数(この場合には変数args)にアクセスできる。

 これに対して、リスト1.5は比較にならない複雑さを持っている。クラスPersonComparerを宣言し、IComparerインターフェースを実装するだけでも行数の増加だが、それだけではない。実は、Compareメソッド内で正順か逆順かを判断するために、コンストラクタで順序の指定を受け取って、値をインスタンス内に保存しておかなければならない。そのためのコードにより、PersonComparerクラスはより大きく膨らんでしまっている。

 そのうえ、ジェネリックもないので、コンパイル時には安全性を確認できないキャストも入ってしまっている。

 以上で最初の問いに答えられるだろう。

 まず、インターフェースを使ってもラムダ式と同じことが可能なのは間違いないが、そのために費やされる文字数が大きく異なる。しかも、本質とは無関係の記述が多い。ソートのための比較機能は、たった1つのメソッドさえ渡せば済む話であるのに、わざわざ本質的に必要のないクラスを宣言したうえで、インターフェースを実装するという余計な手間をかけている。

 これは単にラムダ式を使うと文字数が少ないという話ではない。本質と関係ない記述を極限まで切り落としたシンプルな記述を可能にした、ということである。それゆえに、このケースでは、インターフェースを使うよりも、ラムダ式を使うほうが可読性や保守性の高いソースコードが得られていると見るべきだろう。


 INDEX
  [完全版]究極のC#プログラミング
  Chapter1 C# 3.0らしいプログラミングとは?
    1.はじめに/本書の位置づけ
    2.1.1 意外性あり? 本書で解説すること/C# 3.0の適用範囲/筆者の来歴
    3.1.2 C# 3.0らしいソースコードとは?
    4.1.3 コードの遅延実行という例
  5.1.4 インターフェースとの比較
    6.1.5 後退するクラスの立場
    7.1.6 クラスベースとプロトタイプベース
    8.1.7 クラスベースの問題点/【C#olumn】クラスの問題とは何か?
    9.1.8 JavaScriptとの相違点
    10.まとめ/【C#olumn】金のハンマーと銀の弾丸―クラス至上主義
 
インデックス・ページヘ  「[完全版]究極の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 記事ランキング

本日 月間