今回はデリゲート(delegate)とラムダ式についてです。
イベントやLINQを使おうとすると出てくる用語ですね。
この2つがどういう関係かというと、
「デリゲート型の変数や引数にラムダ式を使って値を代入する」ということをします。
デリゲート(Delegete)とは?
簡単に言うとデリゲートとはメソッドの参照を入れるための型です。
何のためにあるかというと、処理を他のメソッドに任せるためにあります。
C#のLINQという機能の中にWhereというメソッドがあります。
リスト(List)などのコレクションから条件に一致した要素を取り出すというメソッドなのですが、Whereには引数に「条件を判定するためのメソッド」を指定する必要があります。
Whereメソッドはコレクションから順番に要素を取り出すのはやるんで、いるいらないの処理は任せるねというメソッドになります。
イベントはイベントが発生したからあとは任せるねという機能になります。
イベントが発生したときに行うメソッドを指定しておく感じです。
このように処理を渡されたメソッドに任せるのでDeletgete(委任、人に任せるという意味)と呼ばれます。
これの何がいいのか?というと、処理(Whereメソッドの場合は条件)を自分で自由に決められるということです。
で、メソッドを引数に渡すためにはメソッドをオブジェクトとして扱う(変数に入れれるようにする)必要があります。そのためにC#が用意したのがデリゲートと呼ばれるデータ型です。
デリゲート型とは?
メソッドの型は、引数と戻り値によって決まります。
「引数なしで戻り値なし」というメソッドの型、
「引数がint型で戻り値なし」というメソッドの型、
「引数なしで戻り値がint型」というメソッドの型、
「引数がint型で戻り値がstring型」というメソッドの型、
というようにいろんなパターンが考えられます。
C#はデリゲートを簡単にする方法を用意しています。
それが 汎用デリゲートのAction と Func です。
汎用デリゲートActionについて
Action を使うと戻り値がvoid、つまり戻り値のないメソッド型の宣言が簡単にできます。
先ほどの、
「引数なしで戻り値なし」というメソッドの型、
「引数がint型で戻り値なし」というメソッドの型、
という型を Action を使うとこんな感じで書くことができます。
using System;
class Program
{
static void Main(string[] args)
{
// 「引数なしで戻り値なし」というメソッドの型
Action delegete1;
// 「引数がint型で戻り値なし」というメソッドの型
Action<int> delegate2;
}
}
Actionの構文は、
引数なしの場合、「 Action 変数名; 」
引数ありの場合、「 Action<引数の型> 変数名; 」という感じです。
複数の引数の指定は Actionの後ろに < > を付けてその中に引数のデータ型をカンマ区切りで指定します。
「引数1がint型、引数2がstring型で戻り値なし」というメソッドの型はこんな感じになります。
Action<int, string> 変数名;
Actionの引数は最大16個まで指定することが出来ます。
汎用デリゲートFuncについて
Func を使うと戻り値を返すメソッドの型の宣言を簡単にできます。
先ほどの、
「引数なしで戻り値がint型」というメソッドの型、
「引数がint型で戻り値がstring型」というメソッドの型、
という型を Func を使うとこんな感じで書くことができます。
using System;
class Program
{
static void Main(string[] args)
{
// 「引数なしで戻り値がint型」というメソッドの型
Func<int> delegete1;
// 「引数がint型で戻り値がstring型」というメソッドの型
Func<int, string> delegate2;
}
}
Func の構文は、
引数なしの場合、「 Func<戻り値の型> 変数名; 」
引数ありの場合、「 Func<引数の型, 戻り値の型> 変数名; 」という感じです。
Funcの後ろの < > の中に引数の型をカンマ区切りで指定し、最後に戻り値の型を指定します。
Funcの引数は最大16個指定することができます。こんな感じです。
using System;
class Program
{
static void Main(string[] args)
{
// 引数が16個、最後に戻り値 で計17個が最大
Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, string> delegete3;
}
}
汎用デリゲートに値を代入する
Action、Funcで宣言した変数は値を代入しないと使えません。
この場合の値とは、Action、Funcで指定した引数、戻り値を持つメソッドになります。
なので、デリゲート型に合うようなメソッドを定義して変数に代入します。
using System;
class Program
{
static void method1()
{
Console.WriteLine("1.「引数なしで戻り値なし」というメソッドです");
}
static void method2(int a)
{
Console.WriteLine("2.「引数がint型で戻り値なし」というメソッドです");
}
static int method3()
{
Console.WriteLine("3.「引数なしで戻り値がint型」というメソッドです");
return 10;
}
static string method4(int a)
{
Console.WriteLine("4.「引数がint型で戻り値がstring型」というメソッドです");
return "戻り値";
}
static void Main(string[] args)
{
// 1.「引数なしで戻り値なし」というメソッドの型
Action delegete1 = method1;
// 2.「引数がint型で戻り値なし」というメソッドの型
Action<int> delegate2 = method2;
// 3.「引数なしで戻り値がint型」というメソッドの型
Func<int> delegete3 = method3;
// 4.「引数がint型で戻り値がstring型」というメソッドの型
Func<int, string> delegate4 = method4;
// 変数に代入したメソッドを実行する
delegete1();
delegate2(0);
var result1 = delegete3();
var result2 = delegate4(0);
}
}
とても面倒くさいですね。
ここでもC#が簡単に書く方法を用意しています。
ラムダ式の登場です。
ラムダ式について
ラムダ式を使うとその場限りのメソッド(匿名関数)を作ることができます。
これで、いちいちメソッドを定義することなく、デリゲート型に合ったメソッドを代入することができます。
上のサンプルコードをラムダ式で書き直すとこんな感じになります。
using System;
class Program
{
static void Main(string[] args)
{
// 1.「引数なしで戻り値なし」というメソッドの型
Action delegete1 = () => Console.WriteLine("1.「引数なしで戻り値なし」というメソッドです");
// 2.「引数がint型で戻り値なし」というメソッドの型
Action<int> delegate2 = a => Console.WriteLine("2.「引数がint型で戻り値なし」というメソッドです");
// 3.「引数なしで戻り値がint型」というメソッドの型
Func<int> delegete3 = () => {
Console.WriteLine("3.「引数なしで戻り値がint型」というメソッドです");
return 10;
};
// 4.「引数がint型で戻り値がstring型」というメソッドの型
Func<int, string> delegate4 = (a) => {
Console.WriteLine("4.「引数がint型で戻り値がstring型」というメソッドです");
return "戻り値";
};
// 変数に代入したメソッドを実行する
delegete1();
delegate2(0);
var result1 = delegete3();
var result2 = delegate4(0);
}
}
大分すっきりしましたね。ではラムダ式の使い方です。
ラムダ式の構文は、
引数なしの場合、「 ( ) => { 処理 } 」
引数ありの場合、「 (引数名) => { 処理 } 」という感じです。
ラムダ演算子 => の左辺に引数名、右辺に処理を書きます。
左辺には ( ) の中に引数名をカンマ区切りで指定します。引数の型はC#が自動で代入しようとしているデリゲート型から判断するのでいりません。
右辺には { } の中に普通のメソッドと同じように処理を書きます。
引数が1つの場合、左辺の ( ) を省略することができます。
処理が1つの場合、右辺の { } と return を省略することができます。
サンプルコードの10行目2.「引数がint型で戻り値なし」というメソッドの型 のところで、( ) と { } を省略しています。
サンプルコードの19~22行目で、”戻り値”というstring型の戻り値を返すだけをいうメソッドにした場合、
Func<int, string> delegate4 = a => "戻り値";
こんな感じで、{ } と return を省略することが出来ます。
ラムダ式の詳しい使い方は、[C# 入門] 匿名関数(ラムダ式)の使い道を見てください。
引数にデリゲート型を持つメソッドに値を渡す
最初に登場したLINQのWhereメソッドの引数にラムダ式を使って値を渡してみます。
ラムダ式を使うとその場でメソッドを作って渡すことができるので、わざわざWhereに渡すためだけにメソッドの定義を別の場所に書かなくて済みます。
引数にはコレクションの要素のデータ型の引数でbool型の値を返すメソッドを指定します。
using System;
using System.Linq;
using System.Collections.Generic;
class Program
{
public static void Main()
{
var list = new List<int> { 1, 2, 3, 4, 5, 6 };
// ラムダ式を使ってデリゲート型の引数に値を渡す
var odd = list.Where(i => i % 2 == 1);
Console.WriteLine($"odd={string.Join(",", odd)}");
}
}
引数にデリゲート型を持つメソッドを作る
自分で引数にデリゲート型を持つメソッドを作ることもできます。
using System;
class Program
{
public static void Main()
{
// メソッドのデリゲート型引数にラムダ式を使って代入
testMethod1((i) => { Console.WriteLine(i); });
}
// int型の引数を持つメソッドが引数のメソッド
static void testMethod1(Action<int> act) {
// デリゲート型の引数を実行
act(1);
// Invodeメソッドを使っても実行することができる
act.Invoke(2);
}
}
C# 記事まとめページに戻る(他のサンプルコードもこちら)
コメント