ラムダ式 (C# プログラミング ガイド)Lambda expressions (C# Programming Guide)

"ラムダ式" は、次の 2 つのいずれかの形式を持つ式です。A lambda expression is an expression of any of the following two forms:

ラムダ宣言演算子=>を使用して、ラムダのパラメーター リストを式本体から分離します。Use the lambda declaration operator => to separate the lambda's parameter list from its body. ラムダ式を作成するには、ラムダ演算子の左辺に入力パラメーターを指定し (ある場合)、右辺に式またはステートメント ブロックを指定します。To create a lambda expression, you specify input parameters (if any) on the left side of the lambda operator and an expression or a statement block on the other side.

ラムダ式は、デリゲート型に変換できます。Any lambda expression can be converted to a delegate type. ラムダ式を変換できるデリゲート型は、パラメータと戻り値の型で定義されます。The delegate type to which a lambda expression can be converted is defined by the types of its parameters and return value. ラムダ式が値を返さない場合は Action デリゲート型のいずれかに変換でき、値を返す場合はFunc デリゲート型のいずれかに変換できます。If a lambda expression doesn't return a value, it can be converted to one of the Action delegate types; otherwise, it can be converted to one of the Func delegate types. たとえば、2 つのパラメーターがあり、値を返さないラムダ式は、Action<T1,T2> デリゲートに変換できます。For example, a lambda expression that has two parameters and returns no value can be converted to an Action<T1,T2> delegate. 1 つのパラメーターがあり、値を返すラムダ式は、Func<T,TResult> デリゲートに変換できます。A lambda expression that has one parameter and returns a value can be converted to a Func<T,TResult> delegate. 次の例では、x という名前のパラメーターを指定し、x の二乗値を返すラムダ式 x => x * x をデリゲート型の変数に割り当てます。In the following example, the lambda expression x => x * x, which specifies a parameter that’s named x and returns the value of x squared, is assigned to a variable of a delegate type:

Func<int, int> square = x => x * x;
Console.WriteLine(square(5));
// Output:
// 25

式形式のラムダは、次の例に示すように、式ツリー型にも変換できます。Expression lambdas also can be converted to the expression tree types, as the following example shows:

System.Linq.Expressions.Expression<Func<int, int>> e = x => x * x;
Console.WriteLine(e);
// Output:
// x => (x * x)

ラムダ式は、デリゲート型または式ツリーのインスタンスを必要とするすべてのコードで使用できます。たとえば、Task.Run(Action) メソッドの引数として使用すると、バックグラウンドで実行する必要があるコードを渡すことができます。You can use lambda expressions in any code that requires instances of delegate types or expression trees, for example as an argument to the Task.Run(Action) method to pass the code that should be executed in the background. また、次の例に示すように、LINQ クエリ式を作成する場合にもラムダ式を使用できます。You also can use lambda expressions when you write LINQ query expressions, as the following example shows:

int[] numbers = { 2, 3, 4, 5 };
var squaredNumbers = numbers.Select(x => x * x);
Console.WriteLine(string.Join(" ", squaredNumbers));
// Output:
// 4 9 16 25

メソッド ベースの構文を使用して System.Linq.Enumerable クラス (たとえば、LINQ to Objects、LINQ to XML など) の Enumerable.Select メソッドを呼び出すと、パラメーターはデリゲート型 System.Func<T,TResult> になります。When you use method-based syntax to call the Enumerable.Select method in the System.Linq.Enumerable class, for example in LINQ to Objects and LINQ to XML, the parameter is a delegate type System.Func<T,TResult>. System.Linq.Queryable クラス (たとえば、LINQ to SQL など) の Queryable.Select メソッドを呼び出すと、パラメーター型は式ツリー型 Expression<Func<TSource,TResult>> になります。When you call the Queryable.Select method in the System.Linq.Queryable class, for example in LINQ to SQL, the parameter type is an expression tree type Expression<Func<TSource,TResult>>. どちらの場合も、同じラムダ式を使用してパラメーター値を指定できます。In both cases you can use the same lambda expression to specify the parameter value. これにより、2 つの Select 呼び出しは外観が似ていますが、ラムダから実際に作成されるオブジェクトの型は異なります。That makes the two Select calls to look similar although in fact the type of objects created from the lambdas is different.

式形式のラムダExpression lambdas

=> 演算子の右辺に式があるラムダ式を "式形式のラムダ" と呼びます。A lambda expression with an expression on the right side of the => operator is called an expression lambda. 式形式のラムダは、式ツリーの構築に幅広く使用されます。Expression lambdas are used extensively in the construction of expression trees. 式形式のラムダは式の結果を返します。基本的な形式は次のとおりです。An expression lambda returns the result of the expression and takes the following basic form:

(input-parameters) => expression

かっこはラムダの入力パラメーターが 1 つの場合のみ省略可能で、それ以外の場合は必須です。The parentheses are optional only if the lambda has one input parameter; otherwise they are required.

入力パラメーターがないことを指定するには、次のように空のかっこを使用します。Specify zero input parameters with empty parentheses:

Action line = () => Console.WriteLine();

入力パラメーターが 2 つ以上ある場合は、かっこで囲んで各パラメーターをコンマで区切ります。Two or more input parameters are separated by commas enclosed in parentheses:

Func<int, int, bool> testForEquality = (x, y) => x == y;

コンパイラによる入力の型の推論が不可能な場合があります。Sometimes it's impossible for the compiler to infer the input types. 次の例のように、型を明示的に指定できます。You can specify the types explicitly as shown in the following example:

Func<int, string, bool> isTooLong = (int x, string s) => s.Length > x;

入力パラメーターの型は、すべて明示的またはすべて暗黙的である必要があります。それ以外の場合は、CS0748 コンパイラ エラーが発生します。Input parameter types must be all explicit or all implicit; otherwise, a CS0748 compiler error occurs.

式形式のラムダの本体を、メソッド呼び出しで構成できます。The body of an expression lambda can consist of a method call. ただし、SQL Server などの .NET 共通言語ランタイムの外部で評価される式ツリーを作成する場合は、ラムダ式内でメソッド呼び出しを使用することはできません。However, if you are creating expression trees that are evaluated outside the context of the .NET common language runtime, such as in SQL Server, you should not use method calls in lambda expressions. .NET 共通言語ランタイムのコンテキストの外部では、これらのメソッドは通用しません。The methods will have no meaning outside the context of the .NET common language runtime.

ステートメント形式のラムダStatement lambdas

ステートメント形式のラムダは式形式のラムダに似ていますが、ステートメントが中かっこで囲まれる点が異なります。A statement lambda resembles an expression lambda except that the statement(s) is enclosed in braces:

(input-parameters) => { <sequence-of-statements> }

ステートメント形式のラムダの本体は任意の数のステートメントで構成できますが、実際面では通常、2、3 個以下にします。The body of a statement lambda can consist of any number of statements; however, in practice there are typically no more than two or three.

Action<string> greet = name => 
{ 
    string greeting = $"Hello {name}!";
    Console.WriteLine(greeting);
};
greet("World");
// Output:
// Hello World!

ステートメント形式のラムダを使用して式ツリーを作成することはできません。Statement lambdas cannot be used to create expression trees.

非同期ラムダAsync lambdas

async キーワードと await キーワードを使用すると、非同期処理を組み込んだラムダ式およびステートメントを簡単に作成できます。You can easily create lambda expressions and statements that incorporate asynchronous processing by using the async and await keywords. たとえば、次に示す Windows フォーム例には、非同期メソッド ExampleMethodAsyncを呼び出して待機するイベント ハンドラーが含まれています。For example, the following Windows Forms example contains an event handler that calls and awaits an async method, ExampleMethodAsync.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        button1.Click += button1_Click;
    }

    private async void button1_Click(object sender, EventArgs e)
    {
        await ExampleMethodAsync();
        textBox1.Text += "\r\nControl returned to Click event handler.\n";
    }

    private async Task ExampleMethodAsync()
    {
        // The following line simulates a task-returning asynchronous process.
        await Task.Delay(1000);
    }
}

非同期ラムダを使用して、同じイベント ハンドラーを追加できます。You can add the same event handler by using an async lambda. このハンドラーを追加するには、次の例に示すように、ラムダ パラメーター リストの前に async 修飾子を追加します。To add this handler, add an async modifier before the lambda parameter list, as the following example shows:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        button1.Click += async (sender, e) =>
        {
            await ExampleMethodAsync();
            textBox1.Text += "\r\nControl returned to Click event handler.\n";
        };
    }

    private async Task ExampleMethodAsync()
    {
        // The following line simulates a task-returning asynchronous process.
        await Task.Delay(1000);
    }
}

非同期メソッドの作成および使用方法の詳細については、「Async および Await を使用した非同期プログラミング」を参照してください。For more information about how to create and use async methods, see Asynchronous Programming with async and await.

ラムダ式とタプルLambda expressions and tuples

C# 7.0 以降、C# 言語には、タプルのサポートが組み込まれています。Starting with C# 7.0, the C# language provides built-in support for tuples. タプルは、ラムダ式への引数として指定できるほか、ラムダ式で返すこともできます。You can provide a tuple as an argument to a lambda expression, and your lambda expression can also return a tuple. 場合によっては、C# コンパイラは、型の推定を使用して、タプル コンポーネントの型を判定することもあります。In some cases, the C# compiler uses type inference to determine the types of tuple components.

タプルを定義するには、そのコンポーネントのコンマ区切りリストをかっこで囲みます。You define a tuple by enclosing a comma-delimited list of its components in parentheses. 次の例では、3 つのコンポーネントを持つタプルを使用して、ラムダ式に連続した数値を渡します。このラムダ式により、各値が 2 倍になり、乗算の結果を格納する 3 つのコンポーネントを持つタプルが返されます。The following example uses tuple with three components to pass a sequence of numbers to a lambda expression, which doubles each value and returns a tuple with three components that contains the result of the multiplications.

Func<(int, int, int), (int, int, int)> doubleThem = ns => (2 * ns.Item1, 2 * ns.Item2, 2 * ns.Item3);
var numbers = (2, 3, 4);
var doubledNumbers = doubleThem(numbers);
Console.WriteLine($"The set {numbers} doubled: {doubledNumbers}");
// Output:
// The set (2, 3, 4) doubled: (4, 6, 8)

通常、タプルのフィールド名は Item1Item2 のようになります。ただし、次の例のとおり、名前付きのコンポーネントを持つタプルを定義することができます。Ordinarily, the fields of a tuple are named Item1, Item2, etc. You can, however, define a tuple with named components, as the following example does.

Func<(int n1, int n2, int n3), (int, int, int)> doubleThem = ns => (2 * ns.n1, 2 * ns.n2, 2 * ns.n3);
var numbers = (2, 3, 4);
var doubledNumbers = doubleThem(numbers);
Console.WriteLine($"The set {numbers} doubled: {doubledNumbers}");

C# のタプルの詳細については、「C# のタプル型」を参照してください。For more information about C# tuples, see C# tuple types.

標準クエリ演算子を使用したラムダLambdas with the standard query operators

いくつかある実装の中で特に、LINQ to Objects は、汎用デリゲートの Func<TResult> ファミリに属する型の入力パラメーターを持ちます。LINQ to Objects, among other implementations, have an input parameter whose type is one of the Func<TResult> family of generic delegates. これらのデリゲートは、型パラメーターを使用して、入力パラメーターの数と型に加え、デリゲートの戻り値の型を定義します。These delegates use type parameters to define the number and type of input parameters, and the return type of the delegate. Func デリゲートは、ソース データのセット内の各要素に適用されるユーザー定義の式をカプセル化する場合に非常に便利です。Func delegates are very useful for encapsulating user-defined expressions that are applied to each element in a set of source data. たとえば、Func<T,TResult>デリゲート型を考えてみましょう。For example, consider the Func<T,TResult> delegate type:

public delegate TResult Func<in T, out TResult>(T arg)

このデリゲートを Func<int, bool> としてインスタンス化できます。 int は入力パラメーター、bool は戻り値です。The delegate can be instantiated as a Func<int, bool> instance where int is an input parameter and bool is the return value. 戻り値は必ず最後の型パラメーターで指定されます。The return value is always specified in the last type parameter. たとえば、Func<int, string, bool> は 2 つの入力パラメーター ( intstring) と戻り値の型 bool を持つデリゲートを定義しています。For example, Func<int, string, bool> defines a delegate with two input parameters, int and string, and a return type of bool. 次の Func デリゲートを呼び出すと、入力パラメーターが 5 に等しいかどうかを示す true または false が返されます。The following Func delegate, when it's invoked, returns Boolean value that indicates whether the input parameter is equal to five:

Func<int, bool> equalsFive = x => x == 5;
bool result = equalsFive(4);
Console.WriteLine(result);   // False

たとえば、Queryable 型で定義された標準クエリ演算子において、引数型が Expression<TDelegate> の場合もラムダ式を使用できます。You can also supply a lambda expression when the argument type is an Expression<TDelegate>, for example in the standard query operators that are defined in the Queryable type. Expression<TDelegate> 引数を指定すると、ラムダは式ツリーにコンパイルされます。When you specify an Expression<TDelegate> argument, the lambda is compiled to an expression tree.

次の例では、Count 標準クエリ演算子を使用します。The following example uses the Count standard query operator:

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int oddNumbers = numbers.Count(n => n % 2 == 1);
Console.WriteLine($"There are {oddNumbers} odd numbers in {string.Join(" ", numbers)}");

入力パラメーターの型はコンパイラが推論できますが、明示的に指定することもできます。The compiler can infer the type of the input parameter, or you can also specify it explicitly. この特定のラムダ式は、2 で除算したときに剰余が 1 になる整数 (n) をカウントします。This particular lambda expression counts those integers (n) which when divided by two have a remainder of 1.

次の例では、numbers 配列内で 9 より前にある要素をすべて含むシーケンスを作成します。これは、9 がシーケンス内で条件を満たさない最初の数値であるためです。The following example produces a sequence that contains all elements in the numbers array that precede the 9, because that's the first number in the sequence that doesn't meet the condition:

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var firstNumbersLessThanSix = numbers.TakeWhile(n => n < 6);
Console.WriteLine(string.Join(" ", firstNumbersLessThanSix));
// Output:
// 5 4 1 3

次の例では、複数の入力パラメーターをかっこで囲んで指定します。The following example specifies multiple input parameters by enclosing them in parentheses. このメソッドは、値が numbers 配列内のその位置を表す序数よりも小さい数値が出現するまで、その配列に含まれるすべての要素を返します。The method returns all the elements in the numbers array until it encounters a number whose value is less than its ordinal position in the array:

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index);
Console.WriteLine(string.Join(" ", firstSmallNumbers));
// Output:
// 5 4

ラムダ式の型の推定Type inference in lambda expressions

ラムダを記述する際、多くの場合は入力パラメーターの型を指定する必要はありません。これは、ラムダ本体やパラメーターの型など C# 言語仕様に記述されている要素に基づいて、コンパイラが型を推定できるためです。When writing lambdas, you often don't have to specify a type for the input parameters because the compiler can infer the type based on the lambda body, the parameter types, and other factors as described in the C# language specification. ほとんどの標準クエリ演算子では、最初の入力がソース シーケンス内の要素の型です。For most of the standard query operators, the first input is the type of the elements in the source sequence. IEnumerable<Customer> を問い合わせると、入力変数は Customer オブジェクトであると推論されます。これは、そのメソッドとプロパティにアクセスできることを意味します。If you are querying an IEnumerable<Customer>, then the input variable is inferred to be a Customer object, which means you have access to its methods and properties:

customers.Where(c => c.City == "London");

ラムダの型の推定の一般規則は、次のとおりです。The general rules for type inference for lambdas are as follows:

  • ラムダにはデリゲート型と同じ数のパラメーターが含まれていなければなりません。The lambda must contain the same number of parameters as the delegate type.

  • ラムダに含まれる各入力パラメーターは、対応するデリゲート パラメーターに暗黙的に変換できなければなりません。Each input parameter in the lambda must be implicitly convertible to its corresponding delegate parameter.

  • ラムダの戻り値 (ある場合) は、デリゲートの戻り値の型に暗黙的に変換できなければなりません。The return value of the lambda (if any) must be implicitly convertible to the delegate's return type.

共通型システムには "ラムダ式" の概念が組み込まれていないため、ラムダ式自体は型を持ちません。Note that lambda expressions in themselves don't have a type because the common type system has no intrinsic concept of "lambda expression." しかし、変則的ではあってもラムダ式の "型" を表現できると都合が良い場合もあります。However, it's sometimes convenient to speak informally of the "type" of a lambda expression. このような場合の型は、ラムダ式の変換後のデリゲート型または Expression 型を指します。In these cases the type refers to the delegate type or Expression type to which the lambda expression is converted.

ラムダ式における外部変数のキャプチャと変数のスコープCapture of outer variables and variable scope in lambda expressions

ラムダでは "外部変数" を参照できます。Lambdas can refer to outer variables. それは、そのラムダ式を定義しているメソッドのスコープ内にある変数、またはそのラムダ式を含む型のスコープ内にある変数です。These are the variables that are in scope in the method that defines the lambda expression, or in scope in the type that contains the lambda expression. こうして取り込まれた変数は、ラムダ式で使用するために格納されます。これは、変数がスコープ外に出てガベージ コレクトされる場合でも変わりません。Variables that are captured in this manner are stored for use in the lambda expression even if the variables would otherwise go out of scope and be garbage collected. 外部変数は、ラムダ式で使用される前に明示的に代入する必要があります。An outer variable must be definitely assigned before it can be consumed in a lambda expression. 次の例は、こうした規則を示しています。The following example demonstrates these rules:

public static class VariableScopeWithLambdas
{
    public class VariableCaptureGame
    {
        internal Action<int> updateCapturedLocalVariable;
        internal Func<int, bool> isEqualToCapturedLocalVariable;

        public void Run(int input)
        {
            int j = 0;

            updateCapturedLocalVariable = x =>
            {
                j = x;
                bool result = j > input;
                Console.WriteLine($"{j} is greater than {input}: {result}");
            };

            isEqualToCapturedLocalVariable = x => x == j;

            Console.WriteLine($"Local variable before lambda invocation: {j}");
            updateCapturedLocalVariable(10);
            Console.WriteLine($"Local variable after lambda invocation: {j}");
        }
    }

    public static void Main()
    {  
        var game = new VariableCaptureGame();
        
        int gameInput = 5;
        game.Run(gameInput);

        int jTry = 10;
        bool result = game.isEqualToCapturedLocalVariable(jTry);
        Console.WriteLine($"Captured local variable is equal to {jTry}: {result}");

        int anotherJ = 3;
        game.updateCapturedLocalVariable(anotherJ);

        bool equalToAnother = game.isEqualToCapturedLocalVariable(anotherJ);
        Console.WriteLine($"Another lambda observes a new value of captured variable: {equalToAnother}");
    }
    // Output:
    // Local variable before lambda invocation: 0
    // 10 is greater than 5: True
    // Local variable after lambda invocation: 10
    // Captured local variable is equal to 10: True
    // 3 is greater than 5: False
    // Another lambda observes a new value of captured variable: True
}

ラムダ式における変数のスコープには、次の規則が適用されます。The following rules apply to variable scope in lambda expressions:

  • 取り込まれた変数は、その変数を参照するデリゲートがガベージ コレクションの対象になるまでガベージ コレクトされません。A variable that is captured will not be garbage-collected until the delegate that references it becomes eligible for garbage collection.

  • ラムダ式内に導入された変数は、外側のメソッドでは参照できません。Variables introduced within a lambda expression are not visible in the enclosing method.

  • ラムダ式は、外側のメソッドから inref、または out パラメーターを直接取り込むことはできません。A lambda expression cannot directly capture an in, ref, or out parameter from the enclosing method.

  • ラムダ式に含まれる return ステートメントで外側のメソッドが戻ることはありません。A return statement in a lambda expression doesn't cause the enclosing method to return.

  • ジャンプ先がラムダ式ブロックの外側にある場合は、ラムダ式に gotobreak、または continue ステートメントを含めることはできません。A lambda expression cannot contain a goto, break, or continue statement if the target of that jump statement is outside the lambda expression block. また、ジャンプ先がブロックの内部にある場合に、ラムダ式ブロックの外部でジャンプ ステートメントを使用するとエラーになります。It's also an error to have a jump statement outside the lambda expression block if the target is inside the block.

C# 言語仕様C# language specification

詳細については、「C# 言語仕様」の無名関数の式に関するセクションを参照してください。For more information, see the Anonymous function expressions section of the C# language specification.

Delegates, Events, and Lambda Expressions (デリゲート、イベント、およびラムダ式)」(『C# 3.0 Cookbook, Third Edition: More than 250 solutions for C# 3.0 programmers (C# 3.0 クックブック (第 3 版): C# 3.0 プログラマ向けの 250 以上のソリューション)』)Delegates, Events, and Lambda Expressions in C# 3.0 Cookbook, Third Edition: More than 250 solutions for C# 3.0 programmers

関連項目See also