属性は.NETのメタデータ処理機能の重要な部分である。属性は、コンパイラ、スタティックアナライザ、ランタイムライブラリによってさまざまな目的で使用される。通常の関数/メソッドは属性を持つことができるが、この提案以前はラムダと無名関数は持つことができなかった。
理論的には、通常の関数/メソッドに適用可能な任意の属性をラムダに適用することができるが、ラムダのために特に考慮されている新しい属性がある。これらの新しい属性は、ラムダが何をキャプチャできるかを制御する。
- CaptureNone: 変数を取り込むことはできない。ラムダは静的関数としてコンパイルされる。
- CaptureThis: ‘this’ポインタだけがキャプチャされる。ラムダはメソッドとしてコンパイルされる。
- CaptureAny: 任意のローカル変数をキャプチャできる。ラムダは無名クロージャオブジェクトの一部としてコンパイルされる。
パフォーマンスに敏感なコードを扱うときは、各インスタンスに対して新しいデリゲートとそれに対応するクロージャオブジェクトを作成する必要があるため、CaptureAnyは避けるべきである。CaptureThisはそこまで悪くないが、それでも新しいデリゲートを割り当てる必要がある。このデリゲートは、外側のオブジェクトを必要以上に長く使用し続けると、メモリの使用効率が悪くなる可能性がある。最後に、CaptureNoneは、アプリケーションのライフタイムを通して再利用できるデリゲートのインスタンスを1つ作成するだけで済む。
ラムダを適用するときには、コンパイラまたはアナライザが属性を使うことで、将来的にCaptureNoneラムダが誤って効率の悪い型に変更されないようにする。基本的に、属性はドキュメントの形式として機能する。
この概念は新しいものではない。たとえば、C++ラムダでは、ラムダがキャプチャできるすべての変数を明示的にリストする必要がある。ここでの違いは、C#では属性がオプションになることである(アナライザーによって強制されない限り)。
この提案では、ラムダのパラメータに属性を追加することもできる。混乱を避けるために、属性を使用するときは、パラメーターを丸括弧で囲まなければならない。属性が丸括弧の内側にもある場合は、パラメータに適用される。丸括弧の外側の属性は、ラムダ全体に適用される。
詳細については、GitHubでのLambda Attributesの提案を参照してください。