簡體   English   中英

不能在 lambda 表達式中使用 ref 或 out 參數

[英]Cannot use ref or out parameter in lambda expressions

為什么不能在 lambda 表達式中使用 ref 或 out 參數?

我今天遇到了這個錯誤並找到了解決方法,但我仍然很好奇為什么這是一個編譯時錯誤。

CS1628 :不能在匿名方法、lambda 表達式或查詢表達式中使用 in 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();
}

Lambda 看起來會改變它們捕獲的變量的生命周期。 例如,下面的 lambda 表達式導致參數 p1 的生存時間比當前方法幀長,因為它的值可以在方法幀不再在堆棧上之后訪問

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

捕獲變量的另一個屬性是變量的更改在 lambda 表達式之外也是可見的。 例如,下面的代碼打印出 42

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

這兩個屬性會產生一組特定的效果,這些效果通過以下方式在ref參數面前飛行:

  • ref參數可能具有固定的生命周期。 考慮將局部變量作為ref參數傳遞給函數。
  • lambda 中的副作用需要在ref參數本身上可見。 在方法中和調用者中。

這些是有些不兼容的屬性,也是 lambda 表達式中不允許使用它們的原因之一。

在后台,匿名方法是通過提升捕獲的變量(這是您的問題主體的全部內容)並將它們存儲為編譯器生成的類的字段來實現的。 無法將refout參數存儲為字段。 Eric Lippert 在一篇博客文章中討論了它。 請注意,捕獲的變量和 lambda 參數之間存在差異。 可以擁有如下“形式參數”,因為它們不是捕獲的變量:

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

您可以,但您必須明確定義所有類型,以便

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

但是無效

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

已驗證

因為這是 Google 上“C# lambda ref”的最佳結果之一; 我覺得我需要擴展上述答案。 較舊的 (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;
        };
}

請記住,Lambda 在程序和數學上更安全(因為前面提到的 ref 值提升):您可能會打開一罐蠕蟲。 使用此語法時請仔細考慮。

也許這個?

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

我再舉一個例子。

描述

下面的代碼將拋出此錯誤。 因為 lambda 表達式(i)=>{...}帶來的變化只在函數test中有效。

static void test(out System.Drawing.Image[] bitmaps)
{
    int count = 10;

    bitmaps = new System.Drawing.Image[count];
    Parallel.For(0, count, (i) =>
    {
        bitmaps[i] = System.Drawing.Image.FromFile("2.bmp");
    });
}

解決方案

因此,如果您out參數,它就可以工作。

static void test(System.Drawing.Image[] bitmaps)
{
    int count = 10;

    bitmaps = new System.Drawing.Image[count];
    Parallel.For(0, count, (i) =>
    {
        bitmaps[i] = System.Drawing.Image.FromFile("2.bmp");
    });
}

如果out需要,請不要直接更改 lambda 表達式中的參數。 相反,請使用臨時變量。

static void test(out System.Drawing.Image[] bitmaps)
{
    int count = 10;

    System.Drawing.Image[] bitmapsTemp = new System.Drawing.Image[count];
    Parallel.For(0, count, (i) =>
    {
        bitmapsTemp[i] = System.Drawing.Image.FromFile("2.bmp");
    });
    bitmaps = bitmapsTemp;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM