|
|
連載:[完全版]究極の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年ずれているので判定式が変わってくる。このようなケースでは、当然のことながら条件演算子は有効である。つまり、この程度であれば、式形式のラムダとして容易に記述できるのである。
Insider.NET 記事ランキング
本日
月間