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

Chapter7 ラムダ式(後編)

川俣 晶
2009/11/02

7.4 式形式のラムダの可能性

 ここで問題になるのは、ソースコードを簡潔にするためにはぜひとも式形式のラムダを使いたいが、書きたいコードがすべて式形式のラムダで書けるわけではない、という点である。

 では、どこまでなら式形式のラムダで記述できるのだろうか?

 C/C++に慣れたプログラマーであれば、構文の似たC#でも式として相当複雑なコードを書けるかもしれないと思うかもしれないが、残念ながらそれはできない。なぜかといえば、C/C++で複雑な機能を持った式を書くための切り札となるカンマ演算子がC#には存在しないためである。

 C/C++であれば、次のようなコードは有効である。

int a,b,c;

c = (a=1, b=2, a+b); // aに1、bに2、cにa + bの値を代入
printf("%d\n",c);

 C/C++では、カンマ演算子を用いてたった1つの式にかなり込み入った処理を記述することができ、それはマクロなどを定義する際に重要な意味を持っていた。

 しかし、このような使い方はC#ではできないのである。

 とはいえ、C#でも条件演算子やnull合体演算子があるので、これらを使うと多少込み入ったコードを式形式のラムダとして記述できる。

 たとえば、「引数で指定したファイル名のファイルに文字列を書き込むが、引数がnullの場合は『default.txt』をファイル名とする」というラムダ式は、次のリスト7.10のようにnull合体演算子(??)を用いて式形式のラムダとして記述できる。

using System;

class Program
{
  static void Main(string[] args)
  {
    Action<string> method =
      (filename) => System.IO File.WriteAllText(
                          filename ?? "default.txt", "Hello!");

    method(null); // default.txtを作成
    method("hello.txt"); // hello.txtを作成
  }
}
リスト7.10 null合体演算子(??演算子)を用いたラムダ式

 あるいは、「引数のフラグがfalseならファイル『normal.log』に追加、trueならファイル『system.log』に追加」であれば、条件演算子(?:)を使って、次のリスト7.11のように式形式のラムダとして記述できる。

using System;

class Program
{
  static void Main(string[] args)
  {
    Action<bool> method =
      (system) => System.IO.File.AppendAllText(
          system ? "system.log" : "normal.log", "log message\r\n");

    method(false); // normal.logを作成
    method(true); // system.logを作成
  }
}
リスト7.11 条件演算子(?:演算子)を用いたラムダ式

 しかし、次ページのリスト7.12に記述した例は、if文を条件演算子に置換できない。

using System;

class Program
{
  static void Main(string[] args)
  {
    // ステートメント型のラムダ
    Action<string> method = (filename) =>
      {
        if (filename == null)
          Console.WriteLine("Hello!");
        else
          System.IO.File.WriteAllText(filename, "Hello!");
      };

    method(null); // 出力:Hello!
    method("hello.txt"); // hello.txtファイルを作成
  }
}
リスト7.12 式形式に書き換えられない例

 もし書き換えを試みるなら、リスト7.13のような内容になろう。

using System;

class Program
{
  static void Main(string[] args)
  {
    Action<string> method = (filename) =>
      filename == null
        ? Console.WriteLine("Hello!")
        : System.IO.File.WriteAllText(filename, "Hello!");

    method(null);
    method("hello.txt");
  }
}
リスト7.13 リスト7.10を条件演算子で書き換えた例(コンパイルエラーとなる)

 このコードは、次のようなコンパイルエラーを発生させる。

error CS0201: 割り当て、呼び出し、インクリメント、デクリメント、および新しいオブジェクトの式のみがステートメントとして使用できます。
error CS0173: 'void' と 'void'' の間に暗黙的な変換がないため、条件式の型がわかりません。

 これは、ラムダ式にvoidを返すメソッド呼び出しを書けないという意味ではない。次のコードは問題なく記述できる。

Action<string> method =
  (filename) => System.IO.File.WriteAllText(filename, "Hello!");

 そうではなく、ここでエラーの原因になっているのは、条件演算子の2番目、3番目にvoid型の式を記述できない(記述しようとしても2番目と3番目の型、つまり、voidの間の暗黙的な変換が存在しないために全体の型を確定できない)という理由による。

 このような問題もあるため、void型を返す式に限っては条件演算子を利用しにくいことになる。もっとも、値を返さない式はわかりにくいので、書けないほうがよいという考え方もありうるだろう。

 ちなみに、当然のことながら、void型以外の型の式であれば、条件演算子は問題なく有効に機能する。次のリスト7.14は問題なくコンパイルでき、実行できる。

using System;

class Program
{
  static void Main(string[] args)
  {
    Func<int, bool> method = (year) =>
      year < 1994 ? year % 4 == 0 : year % 4 == 2;

    Console.WriteLine("冬期オリンピックイヤー");
    for (int i = 1988; i < 1999; i++)
    {
      Console.WriteLine("{0}年={1}", i, method(i));
    }
    // 出力:
    // 冬期オリンピックイヤー
    // 1988年=True
    // 1989年=False
    // 1990年=False
    // 1991年=False
    // 1992年=True
    // 1993年=False
    // 1994年=True
    // 1995年=False
    // 1996年=False
    // 1997年=False
    // 1998年=True
  }
}
リスト7.14 条件演算子を用いたラムダ式

 変数methodに代入したラムダ式は、引数の年が冬期オリンピックの開催年であるかを判定するが、1994年以降は開催年が2年ずれているので判定式が変わってくる。このようなケースでは、当然のことながら条件演算子は有効である。つまり、この程度であれば、式形式のラムダとして容易に記述できるのである。


 INDEX
  [完全版]究極のC#プログラミング
  Chapter7 ラムダ式(後編)
    1.7.1 ラムダ式は何をもたらすか?
    2.7.2 ラムダ式と匿名メソッドの違い
    3.7.3 ステートメント型のラムダ
  4.7.4 式形式のラムダの可能性
    5.7.5 型指定を省略できる場合、できない場合
    6.7.6 何もしないラムダ式
    7.7.7 ラムダ式の使用例/【C#olumn】「=>」は不等号?
    8.7.8 ラムダ式のさまざまなバリエーション
    9.7.9 ジェネリックメソッドと型推論
    10.7.10 オーバーロードの解決/値型と参照型の相違は何か?/練習問題
 
インデックス・ページヘ  「[完全版]究極の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 記事ランキング

本日 月間