#8
by Math » 6年前
C#におけるデリゲート(delegate-委任・委託・委譲という意味)とはその名前の通り、処理の委譲を支援するための仕組みとなります。
委譲とは大辞林によると「権限などを他に任せて譲ること」となっています。
委譲する場合にはきちんと「委譲することができる人」の「条件の定義」が必要となる訳です。
C#においても同じように考えることができて、デリゲートという仕組みでは、まずは処理を委譲することができる関数(メソッド)の条件を定義する必要があります。
そこで、C#のデリゲートでは、まずメソッドの参照を行うために利用する条件メソッドの引数や戻り値を定義することで設定します。 そして定義されたデリゲートはC#では「型」(厳密には参照型)として機能します。
それでは定義方法です。定義は「delegate」という予約語を用いて「メソッド」の「引数」や「戻り値」などを指定します。
前述のとおり、デリゲートは型として定義することになるので、定義したデリゲートを用いることで、メソッドを格納するための「変数宣言」を行うことができる訳ですね。
以下のサンプルでは「MyDelegate」という名前のデリゲートを定義しています。
ここではintの戻り値を持ち、int型の引数を持つデリゲートはこんな感じで定義します。
// デリゲートを定義
delegate int MyDelegate(int x); // delegate 戻り値の型 デリゲート名(パラメータリスト);
次に宣言したdelegateを用いて変数宣言を行って、それを利用する訳です。
デリゲートで変数宣言した変数には「メソッドそのもの」を代入することが可能です。
そして、代入した変数を用いてメソッドを呼び出すことが可能になります。
デリゲート(C# 2.0)
基本的なデリゲートの機能的には1.0と大差ないのですが、デリゲートの代入時にインスタンス生成(new デリゲート名)を省略できるようになりました。
匿名メソッド・匿名関数(C# 2.0)
また、C# 2.0では匿名メソッド(anonymous method)・インラインメソッド(inline method)が追加されていました。
インラインメソッドとはdelegate構文を用いて「メソッド名を持たない処理」を記述する方法が提供されました。
// Delegate
public delegate int CalculateMethod(int x, int y);
static void Main(string[] args)
{
// 匿名デリゲート
CalculateMethod calc = delegate(int x, int y) { return x + y; };
Console.WriteLine(calc(10, 20));
}
delegateを用いて非常に簡単は処理を記述することに利用されていましたが、今では後述するラムダ式があるため、利用する機会は減りました。
定義済みデリゲート型(共通デリゲート型)(C# 3.0/.NET Framework 3.5)
C# 2.0や.NET Framework 2.0で導入したジェネリック(総称性)の機能を用いて、.NET Framework 3.5では以下の3種類のデリゲートが定義されました。
戻り値のない定義済みデリゲートのAction
•戻り値を持つ定義済みデリゲートのFunc
•論理型のも戻り値を持つ定義済みデリゲートのPredicate
具体的にどのように定義されているかというと
public delegate void Action()
public delegate void Action<in T>(T arg)
public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2)
public delegate void Action<in T1, in T2, in T3>(T1 arg1, T2 arg2, T3 arg3)
public delegate void Action<in T1, in T2, in T3, in T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
public delegate TResult Func<out TResult>()
public delegate TResult Func<in T, out TResult>(T arg)
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2)
public delegate TResult Func<in T1, in T2, in T3, out TResult>(T1 arg1, T2 arg2, T3 arg3)
public delegate TResult Action<in T1, in T2, in T3, in T4, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
public delegate bool Predicate<T>(T arg)
というような感じで定義されています。
実は、.NET Framework 2.0で既にAction<T>(T arg)のみ定義されていましたが、その後3.0で、上記のように引数の数が4つあるジェネリックデリゲートまで定義されました。
最新の.NET Framework 4.5ではActionやFuncに指定される引数の数が最大16まであるデリゲートが定義されています。
そのため2.0までに行っていたように新しく「delegateキーワードを使ったデリゲート宣言」を行わなくてよくなり、多くのケースで定義済みのデリゲートを利用することで事足りるようになりました。
class Sample
{
public int Add(int x, int y)
{
return x + y;
}
public void Write(int s)
{
Console.WriteLine(s);
}
}
class Program
{
static void Main(string[] args)
{
Sample s = new Sample();
// 戻り値のないメソッドをActionデリゲートで
Action<int> write = s.Write;
// 戻り値がある関数はFuncデリゲートで
Func<int, int, int> calc1 = s.Add;
// 匿名関数を使って宣言
Func<int, int, int> calc2 = delegate(int x, int y) { return x - y; };
write(calc1(10, 20));
write(calc2(10, 20));
}
}
ラムダ式(C# 3.0)
ラムダ式は簡単に言ってしまうと「デリゲート型」および「式ツリー(ExpressionTree)」を作成するために使用できる匿名関数です。
主にLINQで用いることができるよう追加された仕様ですが、もちろんそれ以外の用途でも利用できます。
ラムダ式を作成するにはラムダ演算子( => ) を用いて左辺にパラメータ、右辺に式やステートメントブロックを置くことで作成できます。
// =>の左側がパラメータ、右側が式
Func<int, int> fuctorial = x => x*x;
// パラメータが複数ある場合は()の中に指定する
Func<int, int, int> calc2 = (x, y) => x + y;
// 複数のステートメントになる場合はステートメントブロックを使って、
// メソッドと同様return文で戻り値を指定する。
Func<int, int, int> calc3 = (x, y) =>
{
int result = x - y;
return result;
};
上記のようにラムダ式には式形式のものとステートメント形式のものがあります。
また、匿名関数といっても、前述した匿名関数とは見た目以外にもいくつかの違いがあります。
•ラムダ式の戻り値はデリゲート以外にも式ツリー(Expressionクラスを用いたツリー)も作成が可能。ただし、ラムダ式内でメソッド呼出などを行うと式ツリーの作成ができない。
•匿名関数は、インラインで記述できるがメソッドなので、ステートメントブロックの中から定義されているメソッド内の変数にはアクセスできませんが、ラムダ式では「変数のキャプチャ」という機能により下記のサンプルのように上位スコープの変数のアクセスが行える。
Action<string> GetWriteAction(strings)
{
string text =string.Format(“[{0}]”);
// 上位スコープのtextをキャプチャ
return p =>Console.WriteLine(“{0}:{1}“, p, text);
}
ただし、上記のサンプルのように変数をキャプチャした状態で外部メソッドをデリゲートを引き継ぐと、通常メソッド終了時に変数の解放がされるところ、生存期間が長くなることがありますので注意が必要です。
現在のC#の定義
ほとんどのケースで必要なし(定義済みデリゲートを利用)
現在のC#での利用
Func<int, int, int> add = (x, y) => x + y;
Func<int, int, int> sub = (x, y) =>
{
int result = x - y;
return result;
};
このようにかなり簡潔に記述できるようになりました。
C#におけるデリゲート(delegate-委任・委託・委譲という意味)とはその名前の通り、処理の委譲を支援するための仕組みとなります。
委譲とは大辞林によると「権限などを他に任せて譲ること」となっています。
委譲する場合にはきちんと「委譲することができる人」の「条件の定義」が必要となる訳です。
C#においても同じように考えることができて、デリゲートという仕組みでは、まずは処理を委譲することができる関数(メソッド)の条件を定義する必要があります。
そこで、C#のデリゲートでは、まずメソッドの参照を行うために利用する条件[color=#FFBF00]メソッド[/color]の[color=#FF8080]引数[/color]や[color=#FF8080]戻り値[/color]を定義することで設定します。 そして定義されたデリゲートはC#では「型」(厳密には参照型)として機能します。
それでは定義方法です。定義は「delegate」という予約語を用いて「[color=#FFBF00]メソッド[/color]」の「[color=#FF8080]引数[/color]」や「[color=#FF8080]戻り値[/color]」などを指定します。
前述のとおり、デリゲートは型として定義することになるので、定義したデリゲートを用いることで、メソッドを格納するための「変数宣言」を行うことができる訳ですね。
以下のサンプルでは「MyDelegate」という名前のデリゲートを定義しています。
ここではintの戻り値を持ち、int型の引数を持つデリゲートはこんな感じで定義します。
// デリゲートを定義
delegate int MyDelegate(int x); // delegate 戻り値の型 デリゲート名(パラメータリスト);
次に宣言したdelegateを用いて変数宣言を行って、それを利用する訳です。
デリゲートで変数宣言した変数には「メソッドそのもの」を代入することが可能です。
そして、代入した変数を用いてメソッドを呼び出すことが可能になります。
[color=#40FF00]デ[/color][color=#80FF00]リ[/color][color=#BFFF00]ゲ[/color][color=#FFFF00]ー[/color][color=#EABF2B]ト[/color][color=#D48055]([/color][color=#BF4080]C[/color][color=#BF55AA]#[/color] [color=#BF80FF]2[/color][color=#956BFF].[/color][color=#6A55FF]0[/color][color=#4040FF])[/color]
基本的なデリゲートの機能的には1.0と大差ないのですが、デリゲートの代入時にインスタンス生成(new デリゲート名)を省略できるようになりました。
[color=#FF40FF]匿名メソッド・匿名関数(C# 2.0)[/color]
また、C# 2.0では匿名メソッド(anonymous method)・インラインメソッド(inline method)が追加されていました。
インラインメソッドとはdelegate構文を用いて「メソッド名を持たない処理」を記述する方法が提供されました。
// Delegate
public delegate int CalculateMethod(int x, int y);
static void Main(string[] args)
{
// 匿名デリゲート
CalculateMethod calc = delegate(int x, int y) { return x + y; };
Console.WriteLine(calc(10, 20));
}
delegateを用いて非常に簡単は処理を記述することに利用されていましたが、今では後述するラムダ式があるため、利用する機会は減りました。
[color=#40FF00]定[/color][color=#51FF00]義[/color][color=#61FF00]済[/color][color=#72FF00]み[/color][color=#82FF00]デ[/color][color=#93FF00]リ[/color][color=#A4FF00]ゲ[/color][color=#B4FF00]ー[/color][color=#C5FF00]ト[/color][color=#D5FF00]型[/color][color=#E6FF00]([/color][color=#F7FF00]共[/color][color=#FCF706]通[/color][color=#F7E611]デ[/color][color=#F1D51C]リ[/color][color=#ECC527]ゲ[/color][color=#E6B432]ー[/color][color=#E0A43D]ト[/color][color=#DB9348]型[/color][color=#D58253])[/color][color=#D0725F]([/color][color=#CA616A]C[/color][color=#C55175]#[/color] [color=#BF468B]3[/color][color=#BF4B96].[/color][color=#BF51A1]0[/color][color=#BF56AC]/[/color][color=#BF5CB7].[/color][color=#BF61C2]N[/color][color=#BF67CD]E[/color][color=#BF6DD8]T[/color] [color=#BF78EE]F[/color][color=#BF7DF9]r[/color][color=#B97DFF]a[/color][color=#AE78FF]m[/color][color=#A372FF]e[/color][color=#986DFF]w[/color][color=#8D67FF]o[/color][color=#8261FF]r[/color][color=#775CFF]k[/color] [color=#6151FF]3[/color][color=#564BFF].[/color][color=#4B46FF]5[/color][color=#4040FF])[/color]
C# 2.0や.NET Framework 2.0で導入したジェネリック(総称性)の機能を用いて、.NET Framework 3.5では以下の3種類のデリゲートが定義されました。
戻り値のない定義済みデリゲートのAction
•戻り値を持つ定義済みデリゲートのFunc
•論理型のも戻り値を持つ定義済みデリゲートのPredicate
具体的にどのように定義されているかというと
public delegate void Action()
public delegate void Action<in T>(T arg)
public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2)
public delegate void Action<in T1, in T2, in T3>(T1 arg1, T2 arg2, T3 arg3)
public delegate void Action<in T1, in T2, in T3, in T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
public delegate TResult Func<out TResult>()
public delegate TResult Func<in T, out TResult>(T arg)
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2)
public delegate TResult Func<in T1, in T2, in T3, out TResult>(T1 arg1, T2 arg2, T3 arg3)
public delegate TResult Action<in T1, in T2, in T3, in T4, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
public delegate bool Predicate<T>(T arg)
というような感じで定義されています。
実は、.NET Framework 2.0で既にAction<T>(T arg)のみ定義されていましたが、その後3.0で、上記のように引数の数が4つあるジェネリックデリゲートまで定義されました。
最新の.NET Framework 4.5ではActionやFuncに指定される引数の数が最大16まであるデリゲートが定義されています。
そのため2.0までに行っていたように新しく「delegateキーワードを使ったデリゲート宣言」を行わなくてよくなり、多くのケースで定義済みのデリゲートを利用することで事足りるようになりました。
class Sample
{
public int Add(int x, int y)
{
return x + y;
}
public void Write(int s)
{
Console.WriteLine(s);
}
}
class Program
{
static void Main(string[] args)
{
Sample s = new Sample();
// 戻り値のないメソッドをActionデリゲートで
Action<int> write = s.Write;
// 戻り値がある関数はFuncデリゲートで
Func<int, int, int> calc1 = s.Add;
// 匿名関数を使って宣言
Func<int, int, int> calc2 = delegate(int x, int y) { return x - y; };
write(calc1(10, 20));
write(calc2(10, 20));
}
}
[color=#40FF00]ラ[/color][color=#85FF00]ム[/color][color=#CBFF00]ダ[/color][color=#F9EE0C]式[/color][color=#E2A83A]([/color][color=#CB6369]C[/color][color=#BF4C97]#[/color] [color=#BF7AF3]3[/color][color=#9C6FFF].[/color][color=#6E57FF]0[/color][color=#4040FF])[/color]
ラムダ式は簡単に言ってしまうと「デリゲート型」および「式ツリー(ExpressionTree)」を作成するために使用できる匿名関数です。
主にLINQで用いることができるよう追加された仕様ですが、もちろんそれ以外の用途でも利用できます。
ラムダ式を作成するにはラムダ演算子( => ) を用いて左辺にパラメータ、右辺に式やステートメントブロックを置くことで作成できます。
// =>の左側がパラメータ、右側が式
Func<int, int> fuctorial = x => x*x;
// パラメータが複数ある場合は()の中に指定する
Func<int, int, int> calc2 = (x, y) => x + y;
// 複数のステートメントになる場合はステートメントブロックを使って、
// メソッドと同様return文で戻り値を指定する。
Func<int, int, int> calc3 = (x, y) =>
{
int result = x - y;
return result;
};
上記のようにラムダ式には式形式のものとステートメント形式のものがあります。
また、匿名関数といっても、前述した匿名関数とは見た目以外にもいくつかの違いがあります。
•ラムダ式の戻り値はデリゲート以外にも式ツリー(Expressionクラスを用いたツリー)も作成が可能。ただし、ラムダ式内でメソッド呼出などを行うと式ツリーの作成ができない。
•匿名関数は、インラインで記述できるがメソッドなので、ステートメントブロックの中から定義されているメソッド内の変数にはアクセスできませんが、ラムダ式では「変数のキャプチャ」という機能により下記のサンプルのように上位スコープの変数のアクセスが行える。
Action<string> GetWriteAction(strings)
{
string text =string.Format(“[{0}]”);
// 上位スコープのtextをキャプチャ
return p =>Console.WriteLine(“{0}:{1}“, p, text);
}
ただし、上記のサンプルのように変数をキャプチャした状態で外部メソッドをデリゲートを引き継ぐと、通常メソッド終了時に変数の解放がされるところ、生存期間が長くなることがありますので注意が必要です。
[color=#40FF00]現[/color][color=#ADFF00]在[/color][color=#F6E412]の[/color][color=#D1775B]C[/color][color=#BF52A4]#[/color][color=#BF77ED]の[/color][color=#8965FF]定[/color][color=#4040FF]義[/color]
ほとんどのケースで必要なし(定義済みデリゲートを利用)
現在のC#での利用
Func<int, int, int> add = (x, y) => x + y;
Func<int, int, int> sub = (x, y) =>
{
int result = x - y;
return result;
};
このようにかなり簡潔に記述できるようになりました。