web-dev-qa-db-ja.com

ラムダ式でrefまたはoutパラメーターを使用できません

ラムダ式でrefまたはoutパラメーターを使用できないのはなぜですか?

今日エラーに遭遇し、回避策を見つけましたが、なぜこれがコンパイル時エラーであるのかまだ知りませんでした。

CS1628 :匿名メソッド、ラムダ式、またはクエリ式内では、refまたはoutパラメーター 'parameter'で使用できません

以下に簡単な例を示します。

private void Foo()
{
    int value;
    Bar(out value);
}

private void Bar(out int value)
{
    value = 3;
    int[] array = { 1, 2, 3, 4, 5 };
    int newValue = array.Where(a => a == value).First();
}
155
skalb

ラムダは、キャプチャする変数の有効期間を変更するように見えます。たとえば、次のラムダ式は、パラメータp1をliveメソッドフレームがスタック上になくなった後に値にアクセスできるため、現在のメソッドフレームより長くなります

Func<int> Example(int p1) {
  return () => p1;
}

キャプチャされた変数のもう1つのプロパティは、変数の変更がラムダ式の外側でも見えることです。たとえば、次の印刷42

void Example2(int p1) {
  Action del = () => { p1 = 42; }
  del();
  Console.WriteLine(p1);
}

これらの2つのプロパティは、次のようにrefパラメーターに直面して飛ぶ特定の効果セットを生成します

  • refパラメータの有効期間は固定されている場合があります。ローカル変数をrefパラメーターとして関数に渡すことを検討してください。
  • ラムダ内の副作用は、refパラメーター自体に表示される必要があります。メソッド内と呼び出し元の両方。

これらはやや互換性のないプロパティであり、ラムダ式で許可されない理由の1つです。

116
JaredPar

内部では、匿名メソッドはキャプチャされた変数(これはあなたの質問本体です)を巻き上げ、それらをコンパイラのフィールドとして格納することで実装されます生成されたクラス。 refまたはoutパラメーターをフィールドとして保存する方法はありません。 Eric Lippertが ブログエントリ で議論しました。キャプチャされた変数とラムダパラメーターには違いがあることに注意してください。 canは、キャプチャされた変数ではないため、次のような「フォーマルパラメーター」を持つことができます。

delegate void TestDelegate (out int x);
static void Main(string[] args)
{
    TestDelegate testDel = (out int x) => { x = 10; };
    int p;
    testDel(out p);
    Console.WriteLine(p);
}
81
Mehrdad Afshari

ただし、明示的に定義する必要がありますall

(a, b, c, ref d) => {...}

ただし無効です

(int a, int b, int c, ref int d) => {...}

有効です

61
Ben Adams

これは、Googleでの「C#lambda ref」の上位結果の1つです。上記の答えを拡大する必要があると感じています。古い(C#2.0)匿名デリゲート構文は機能し、より複雑な署名をサポートします(クロージャーも同様)。 Lambdaと匿名のデリゲートは、少なくともコンパイラバックエンドで認識された実装を共有しています(それらが同一でない場合)-そして最も重要なのは、クロージャをサポートしていることです。

構文を示すために、検索を実行したときにしようとしていたこと:

public static ScanOperation<TToken> CreateScanOperation(
    PrattTokenDefinition<TNode, TToken, TParser, TSelf> tokenDefinition)
{
    var oldScanOperation = tokenDefinition.ScanOperation; // Closures still work.
    return delegate(string text, ref int position, ref PositionInformation currentPosition)
        {
            var token = oldScanOperation(text, ref position, ref currentPosition);
            if (token == null)
                return null;
            if (tokenDefinition.LeftDenotation != null)
                token._led = tokenDefinition.LeftDenotation(token);
            if (tokenDefinition.NullDenotation != null)
                token._nud = tokenDefinition.NullDenotation(token);
            token.Identifier = tokenDefinition.Identifier;
            token.LeftBindingPower = tokenDefinition.LeftBindingPower;
            token.OnInitialize();
            return token;
        };
}

Lambdasは手続き的にも数学的にも安全であることに注意してください(前述のref値のプロモーションのため):ワームの缶を開けることができます。この構文を使用するときは慎重に検討してください。

5