ラムダ式を使うと名前のないメソッド(匿名関数)を書くことができます。
どういうときに使うのかというと、
- イベントに登録するメソッドを書くときに使う
- タスク(非同期処理)に登録するメソッドを書くときに使う
- LINQのSelect、Whereメソッドのように引数にデリゲート型を持つメソッドに値を渡すのに使う
- メソッド内のデリゲート型の変数に値(メソッド)を代入し、そのメソッド内で何度も呼び出す共通メソッドのように使う
という使い方ができます。
どれも、その処理からしか呼ばれないメソッドをわざわざ定義するのはめんどくさいしメソッドの呼び出し(指定)部分と定義部分が離れてコードに書いてあったら見にくいよね、という場合に使います。
デリゲート?ラムダ式?という人は[C#] デリゲートとラムダ式についてを見てください。
ラムダ式の使い方
ラムダ式は引数のリストと処理ブロックを => でつなげて書きます。
(int a) => {
var b = a * 10;
return b;
}
処理ブロック { } の中が1つの式だけの場合、{ } と return を省略して書くことができます。
(int a) => a * 10
引数リストは、型が推論できる(LINQのSelect、Whereメソッドの引数に指定する時など引数の型が分かっている)場合に、型指定を省略することができます。
// 引数が1つ場合は ( ) も省略できる
a => a * 10
// 引数が2つ以上の場合は ( ) は必須
(x, y) => x * y
引数にデリゲート型を持つメソッドに値を渡す
LINQのSelectメソッドにラムダ式で匿名関数を渡してみます。
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
public static void Main()
{
// ローカル変数a
var a = 2;
// リストの定義
var ary1 = new List<int> { 1, 2, 3, 4, 5 };
// Selectメソッドの引数にラムダ式でメソッドを渡す
var ary2 = ary1.Select(i => i * a);
// 結果を表示
foreach (var i in ary2) Console.WriteLine(i);
}
}
出力結果はこんな感じです。リストの各要素が2倍になりました。
2
4
6
8
10
ここで見てほしいのは、ary1.Select(i => i * a) の a の部分です。
aはサンプルコード9行目で宣言している変数a になります。
何が言いたいのかというと、匿名関数は外側にあるメソッドのローカル変数を使用することが出来るということです。外側にあるローカル変数のことを外部変数といいます。
同じクラスインスタンスのメンバー(フィールド、メソッド、プロパティなど)も外部変数として使うことができます。
また、匿名関数の中から変数aの値を変更することもできます。
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
public static void Main()
{
// ローカル変数a
var a = 2;
// リストの定義
var ary1 = new List<int> { 1, 2, 3, 4, 5 };
// Selectメソッドの引数にラムダ式でメソッドを渡す
var ary2 = ary1.Select(i => i * a++);
// 結果を表示
foreach (var i in ary2) Console.WriteLine(i);
// ローカル変数aを表示
Console.WriteLine($"変数a={a}");
}
}
2
6
12
20
30
変数a=7
メソッドの中でメソッドのように使う
デリゲート型の変数を使うとメソッドの中にメソッドを書くことができます。
デリゲートって何?という人はこちらを見てください。
メソッドの中のデリゲート型の変数は以下のような性質があります。
- 外側(定義しているメソッド内)の外部変数(ローカル変数など)を使用できる
- 定義しているメソッド内からしか使えない(他と同じローカル変数なので)
外部変数を使用できるというのは、上で書いたラムダ式で引数に渡すときと同じです。
また、定義しているメソッド内でしか使えないので、「メソッド内で何度も使いたい処理があるけど他には公開したくない」という時にこのテクニックが役に立ちます。
メソッドAでしか使わない処理を新たにメソッドBとして定義して、他の人が誤って他の場所からメソッドBを呼び出してしまうということ防ぐことが出来ます。
using System;
class Program
{
public static void Main()
{
// デリゲート型の変数にラムダ式で匿名関数を設定
Action<object> act = (o) => Console.WriteLine(o);
// デリゲート型の変数を使って匿名関数を呼び出す
act("デリゲート型の変数にメソッドを入れて");
act("なんども呼び出す");
act("ことができます");
}
}
型推論varが使えるようになった(C#10から)
C#10からvarが使えるようになりました。
using System;
using System.Collections.Generic;
namespace LinqTest;
class Program
{
static void Main()
{
//Action になる
var action1 = () => Console.WriteLine("引数なしのAction");
//Action<int> になる
var action2 = (int x) => Console.WriteLine("引数ありのAction");
//引数の型を明示しないと推論できないのでエラーになる
//var action3 = (x) => Console.WriteLine("引数の型を明示しないとだめ");
//Func<string> になる
var func1 = () => "引数なしのFunc";
//Func<int, string> になる
var func2 = (int x) => "引数ありのFunc";
action1();
action2(10);
Console.WriteLine(func1());
Console.WriteLine(func2(10));
}
}
ラムダ式にオプション引数が指定できるようになった(C#12から)
C#12からラムダ式の引数にオプション引数(デフォルト値)や可変長(params)を指定できるようになりました。
namespace ConsoleApp2
{
internal class Program
{
static void Main(string[] args)
{
//オプション引数(デフォルト値)を指定できる
var lambda1 = (int x = 1) => x + 1;
Console.WriteLine($"lambda1()={lambda1()}");
Console.WriteLine($"lambda1(10)={lambda1(10)}");
//可変長(params)も指定できる
var lambda2 = (params int[] x) => x.Sum();
Console.WriteLine($"lambda2(1, 2, 3)={lambda2(1, 2, 3)}");
}
}
}
lambda1()=2
lambda1(10)=11
lambda2(1, 2, 3)=6
オプション引数についてはこちらの記事をどうぞ
可変長(params)についてはこちら、
匿名関数(ラムダ式)の制限
匿名関数を書く際には以下の制限があります。
- 引数の規定値を持つことが出来ない
- イテレータにすることが出来ない
- ジェネリックメソッドにすることが出来ない
- 引数に ref 、out を付けることが出来ない
上のような制限なくメソッド内にメソッドを定義する方法にローカル関数を使った方法があります。
詳しくは、こちらの記事を見てください。
C# 記事まとめページに戻る(他のサンプルコードもこちら)
コメント