「C# エッセンシャルズ」まとめ その4

2.13 デリゲート (p.70, 71)

 メソッドを関数オブジェクトっぽいものに変換する機能。名前とメソッドシグネチャを含めて宣言を行う。new の引数にメソッドを渡してインスタンスを作成する。
delegate bool Filter(string s);

class Test {
static void Main() {
Filter f = new Filter(FirstHalfOfAlphabet);
Display(new String[] {"Ant", "Lion", "Yak"}, f);
}

static bool FirstHalfOfAlphabet(string s) {
return "N".CompareTo(s) > 0;
}

static void Display(string[] names, Filter f) {
int count = 0;
foreach(string s in names) {
if (f(s)) // デリゲートの呼び出し
Console.WriteLine("Item {0} is {1}", count++, s);
}
}
}

 デリゲートの += メソッドを使って、複数のメソッドの保存と呼び出しを行うことができる。
using System;
delegate void MethodInvoker();
class Test {
static void Main() {
new Test(); //=> "Foo", "Goo"
}
Test () {
MethodInvoker m = null;
m += new MethodInvoker(Foo);
m += new MethodInvoker(Goo);
}
void Foo() {
Console.WriteLine("Foo");
}
void Goo() {
Console.WriteLine("Goo");
}
}


以下、「C#エッセンシャルズ」には無い話。

C# 2.0 からは メソッドからデリゲートへの暗黙の変換が可能になったらしい。上の例は、こう書ける。
delegate bool Filter(string s);

class Test {
static void Main() {
Filter f = FirstHalfOfAlphabet; // 暗黙に変換
Display(new String[] {"Ant", "Lion", "Yak"}, f);
}

static bool FirstHalfOfAlphabet(string s) {
return "N".CompareTo(s) > 0;
}

static void Display(string[] names, Filter f) {
int count = 0;
foreach(string s in names) {
if (f(s)) // デリゲートの呼び出し
Console.WriteLine("Item {0} is {1}", count++, s);
}
}
}

匿名メソッド
 C# 2.0 から匿名メソッドというものが導入された、、、が、もっと便利な構文 (ラムダ式) が C# 3.0 から導入されたようなので、省略。

ラムダ式
基本形 (式形式)
(input parameters) => expression

 パラメータの括弧は、パラメータの数が1つの場合は省略可能。パラメータの型は、コンパイラによる型推論が可能な場合は省略可能。
x => x > 0;
(x, y) => return x + y;
(int x, string s) => s.Length > x

本体部分が複数のステートメントからなる場合は、中括弧でくくる (ステートメント形式)。
n => {
string s = n + " " + "World";
Console.WriteLine(s);
}

標準ジェネリックデリゲート
基本形
Func<TParam1, TParam2, ..., TResult>

TParam1やTParam2 に引数の型を、Tresult に返り値の型を指定する。これを使って、最初の例を書き直してみると、
class Test {
static void Main() {
Display(new String[] {"Ant", "Lion", "Yak"},
s => "N".CompareTo(s) > 0); // ここがラムダ式
}

// 標準ジェネリックデリゲートでラムダ式を受け取る
static void Display(string[] names, Func f) {
int count = 0;
foreach(string s in names) {
if (f(s)) // デリゲートの呼び出し
Console.WriteLine("Item {0} is {1}", count++, s);
}
}
}
最初の例と比べると、かなり簡潔に書けるようになった。

 Java陣営が、クロージャを導入するとかしないとか言っている間に (結局、JDK7 には入らないみたいだけど) C# の関数型機能がこんなに充実しているのに驚き。

参考:
デリゲート (C# によるプログラミング入門)
関数型言語由来の新機能 (C# によるプログラミング入門)
MSDN: Func(T, TResult) デリゲート (System)
[PR]
by fkmn | 2009-04-13 23:55 | IT
<< Remedie 入れてみた 「C# エッセンシャルズ」まと... >>


とあるWebアプリケーションエンジニアの日記
S M T W T F S
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
カテゴリ
以前の記事
ブログパーツ
リンク
検索
タグ
最新のトラックバック
プログラミングが「出来る..
from とりあえず9JP?
Genographic ..
from ナンジャモンジャ
ジュセリーノ
from ありの出来事
くちこみブログ集(ライフ..
from くちこみブログ集(ライフ)(..
以降、丁寧語で行こう!
from エッセイ的な何か
その他のジャンル
ファン
記事ランキング
ブログジャンル
画像一覧

fkmnの最近読んだ本 フィードメーター - フッ君の日常 あわせて読みたい AX