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


172

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

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

CS1628:匿名メソッド、ラムダ式、またはクエリ式内で参照または出力パラメーター '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();
}

それはイテレータについてですが、この記事での同じ理由のほとんど(エリックリッペルトによる&mdash;彼は結局言語設計チームに属しています)はラムダに適用されます:< blogs.msdn.com/ericlippert/archive/2009/07/13 /… >
Joel Coehoorn、2009

17
あなたが見つけた回避策は何ですか?
Beatles1692

3
ローカルの通常の変数を宣言してそれを操作し、後で結果に値を代入するだけです... var tempValue = value;を追加します。次にtempValueを操作します。
酒に酔ったコードモンキー

回答:


121

ラムダは、キャプチャする変数の有効期間を変更するように見えます。例えば、次のラムダ式は、パラメータP1がせるライブ方法フレームがスタックにもはやされた後、その値にアクセスできないように、より長い現在の方法フレームより

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

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

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

これらの2つのプロパティは、次の方法で参照パラメーターに直面して飛行する特定の効果のセットを生成します。

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

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


35
refラムダ式の中で使用することはできませんが、使用したいという欲求は満たされていません。
zionpi

84

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

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);
}

69

できますが、すべてのタイプを明示的に定義する必要があります。

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

しかし無効です

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

有効です


13
します; 質問はなぜできないのですか。答えはあなたができることです。
ベンアダムス

24
ありません。問題は、ラムダ内で既に定義されている、または定義されている既存の変数を参照できない理由です。サンプルコードを読んだら明らかです(もう一度読んでみてください)。受け入れられた回答は、その理由を明確に説明しています。あなたの答えはラムダの使用またはパラメーターについてです。質問にまったく答えず、他のことについて話すrefoutrefout
edc65

4
@ edc65は正しい...これは質問の件名とは関係ありません。これは、ランバ式(右側)の内容に関するものであり、パラメーターリスト(左側)ではありません。これが26の賛成票を得たのは奇妙です。
ジムバルター2016

6
それは私を助けました。+1。ありがとう
Emad

1
しかし、それがなぜこのように設計されているのかはまだわかりません。すべてのタイプを明示的に定義する必要があるのはなぜですか?意味的に私はする必要はありません。私は何かを失っていますか?
ジョー

5

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

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

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;
        };
}

ラムダは手続き的および数学的に安全であることを覚えておいてください(前述の参照値の昇格のため)。ワームの缶を開く可能性があります。この構文を使用するときは注意深く検討してください。


3
あなたはその質問を誤解したと思います。問題は、ラムダがコンテナメソッド内のref / out変数にアクセスできない理由であり、ラムダ自体に ref / out変数を含めることができない理由ではありませんでした。私の知る限り、後者の正当な理由はありません。今日、私はラムダを書いた(a, b, c, ref d) => {...}ref赤い下線のエラーメッセージ「パラメータ『4』 『REF』キーワードで宣言する必要があります」とありました。Facepalm!PS「参照値プロモーション」とは何ですか?
Qwertie 2014年

1
@Qwertie私はこれを完全なパラメーター化、つまり、a、b、c、およびdの型を含めることで機能させることができました。BenAdamsの回答を参照してください(ただし、元の質問も誤解しています)。
Ed Bayiates、2016

@Qwertie私はそのポイントの半分しか削除しなかったと思います-元のポイントは、ref paramsをクロージャに配置することは危険であるかもしれないということでした私はそれがコンパイルされるかどうかさえ知っています)。
ジョナサンディキンソン

これは、実際に尋ねられた質問とは関係ありません。同様に質問を誤解したベンアダムスからの回答の下で、受け入れられた回答とコメントを参照してください。
ジムBalter

1

そして多分これ?

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

private void Bar(out int value)
{
    value = 3;
    int[] array = { 1, 2, 3, 4, 5 };
    var val = value; 
    int newValue = array.Where(a => a == val).First();
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.