ラムダ式

# ラムダ構文を使用してクロージャーを作成する

クロージャの説明については、備考を参照してください。インターフェースがあるとします:

public interface IMachine<TState, TInput>
{
    TState State { get; }
    public void Input(TInput input);
}

そして、以下が実行されます:

IMachine<int, int> machine = ...;
Func<int, int> machineClosure = i => {
    machine.Input(i);
    return machine.State;
};

machineClosure int の関数を参照します intまで 、舞台裏で IMachine を使用します machine のインスタンス 計算を実行するために参照します。参照 machine であっても machineClosure オブジェクトは維持され、元の IMachine インスタンスは、コンパイラによって自動的に定義される「クロージャ」の一部として保持されます。

警告:これは、同じ関数呼び出しが異なる時点で異なる値を返すことを意味する場合があります (たとえば、この例では、マシンがその入力の合計を保持している場合)。多くの場合、これは予期せぬことであり、関数型スタイルのコードでは避けるべきです。偶発的または予期しないクロージャーはバグの原因となる可能性があります。

# 基本ラムダ式

Func<int, int> add1 = i => i + 1;

Func<int, int, int> add = (i, j) => i + j;

// Behaviourally equivalent to:

int Add1(int i)
{
    return i + 1;
}

int Add(int i, int j)
{
    return i + j;
}

...

Console.WriteLine(add1(42)); //43
Console.WriteLine(Add1(42)); //43
Console.WriteLine(add(100, 250)); //350
Console.WriteLine(Add(100, 250)); //350

# LINQ を使用した基本的なラムダ式

// assume source is {0, 1, 2, ..., 10}

var evens = source.Where(n => n%2 == 0);
// evens = {0, 2, 4, ... 10}

var strings = source.Select(n => n.ToString());
// strings = {"0", "1", ..., "10"}

# ステートメント ブロック本体を含むラムダ構文

Func<int, string> doubleThenAddElevenThenQuote = i => {
    var doubled = 2 * i;
    var addedEleven = 11 + doubled;
    return $"'{addedEleven}'";
};

# System.Linq.Expressions を使用したラムダ式

Expression<Func<int, bool>> checkEvenExpression = i => i%2 == 0;
// lambda expression is automatically converted to an Expression<Func<int, bool>>

# コメント

# 閉鎖

ラムダ式は、使用される変数を暗黙的にキャプチャし、クロージャーを作成します。クロージャーは、いくつかの状態コンテキストに沿った関数です。コンパイラは、ラムダ式が周囲のコンテキストから値を「囲む」たびに、クロージャを生成します。

例えば。以下が実行されたとき

Func<object, bool> safeApplyFiltererPredicate = o => (o != null) && filterer.Predicate(i);

safeApplyFilterPredicate filterer の現在の値へのプライベート参照を持つ新しく作成されたオブジェクトを参照します 、およびその Invoke メソッドは次のように動作します

o => (o != null) && filterer.Predicate(i);

値への参照が現在 safeApplyFilterPredicate にある限り、これは重要な場合があります。 filterer というオブジェクトへの参照が存在します。 現在指している。これはガベージ コレクションに影響し、filterer オブジェクトが 現在参照されている は変異しています。

一方、クロージャーは、他のオブジェクトへの参照を含む動作をカプセル化する効果を意図するために使用できます。

var logger = new Logger();
Func<int, int> Add1AndLog = i => {
    logger.Log("adding 1 to " + i);
    return (i + 1);
};

クロージャーは、ステート マシンのモデル化にも使用できます。

Func<int, int> MyAddingMachine() {
    var i = 0;
    return x => i += x;
};