デリゲート
デリゲート(delegate, 委譲)を使うことで、メソッドをオブジェクトの一種として扱うことができる。
デリゲートを使用するには、delegateキーワードを使ってデリゲート型を定義する。以下では、SomeDelegateというデリゲート型を定義し、変数aにAメソッドを代入している。
using System;
delegate void SomeDelegate(int a);
namespace Sample
{
class Program
{
static void A(int n)
{
Console.WriteLine("A({0}) callesd", n);
}
static void Main(string[] args)
{
SomeDelegate a = A;
a(256);
}
}
}
マルチキャストデリゲート
デリゲートには、+=演算子を用いることで複数のメソッドを代入することができる。複数のメソッドを代入した状態でデリゲート呼び出しを行うと、代入したすべてのメソッドが呼び出される。複数のメソッドを格納した状態のデリゲートのことをマルチキャストデリゲートと呼ぶ。
using System;
delegate void ShowMessage();
namespace Sample
{
class Person
{
string name;
public Person(string name) { this.name = name; }
public void ShowName() { Console.WriteLine("name: {0}", this.name); }
}
class Program
{
static void Main(string[] args)
{
Person p1 = new Person("john");
Person p2 = new Person("smith");
ShowMessage show = new ShowMessage(p1.ShowName);
show += new ShowMessage(p2.ShowName);
show += new ShowMessage(A);
show += new ShowMessage(B);
show();
}
static void A() { Console.WriteLine("A called"); }
static void B() { Console.WriteLine("B called"); }
}
}
標準定義のデリゲート型
デリゲート型は、多くの場合、標準で定義されているActionとFuncだけあれば十分で、デリゲート型を自分で定義する必要はほとんどない。
Actionは戻り値を返さないデリゲートで、Funcは戻り値を返すデリゲートである。
匿名関数
匿名関数は、C#のバージョンアップに伴って、新しく、短い書式が導入されていった。
using System;
using System.Linq;
namespace Sample
{
class Program
{
static void Main(string[] args)
{
var data = new[] { 1, 2, 3 };
var s1 = data.Select(Square); // C# 1.0
var s2 = data.Select(delegate(int x) // C# 2.0
{
return x * x;
});
var s3 = data.Select(x => x * x); // C# 3.0
}
private static int Square(int x)
{
return x * x;
}
}
}
上記コードは『すらすらわかるC#』p.270に掲載されているものだが、本文中に抜けている重要ポイントを補っている。この本のサンプルコードは、ここまではほとんどSystem名前空間さえインポートしていれば動いたが、このサンプルコードはLINQという機能を使っているため、System.Linqをインポートしないと動かない(これに気付かないで30分ほどハマった…)。
C#3.0以上では、基本的にラムダ式を使うべき。簡潔なうえ、ラムダ式にしかできないこともある。
デリゲートの用途
デリゲートは、主に以下のような用途で使う。
- 処理を部分的に差し替える
- 処理をどこか別のタイミング・別の場所で実行してもらう
イベント
イベントハンドラは以下のように定義する。
using System;
namespace Sample
{
class Key { }
class KeyDownEvent
{
public event EventHandler<Key> KeyDown;
private void RaiseKeyDown(Key key)
{
var d = KeyDown;
if (d != null) d(key, null);
}
}
}
静的メソッドと拡張メソッド
C#3.0以降では、静的メソッドをインスタンスメソッドと同じ形式で呼び出す拡張メソッドという機能が利用できる。拡張メソッドは、以下の条件を満たす特別な静的メソッドとして定義する。
- 非ジェネリックな静的クラスのpublicな静的メソッドとして定義する
- 第1引数にthis修飾子をつける
静的メソッドと同様、以下の制約がある。
- private/protectedなメンバーにアクセスできない
- 多態的な動作ができない
using System;
namespace Sample
{
static class Extensions
{
public static int Square(this int x) { return x * x; }
}
class Program
{
static void Main()
{
var x = 3.Square();
Console.WriteLine(x);
}
}
}
なお、拡張メソッドは、現在参照している名前空間から名前とシグネチャが一致するメソッドを探してくる。探索の際のルールは以下。
- 同名の静的メソッドがあると拡張メソッドとして呼び出せない
- 同名の拡張メソッドを定義した異なる名前空間は同時に参照できない
- インスタンスメソッドが優先される
- 同じ名前空間のものが優先される