簡體   English   中英

在關閉警告中訪問foreach變量

[英]Access to foreach variable in closure warning

我收到以下警告:

在關閉時訪問foreach變量。 使用不同版本的編譯器編譯時可能會有不同的行為。

這就是我的編輯器中的樣子:

懸停彈出窗口中的上述錯誤消息

我知道怎么修這個警告,但我想知道為什么我會收到這個警告?

這是關於“CLR”版本的嗎? 它與“IL”有關嗎?

這個警告有兩個部分。 首先是......

在關閉時訪問foreach變量

......本身並非無效,但乍一看是違反直覺的。 這樣做也很難。 (以至於我鏈接到下面的文章將其描述為“有害”。)

接受您的查詢,注意您摘錄的代碼基本上是C#編譯器(在C#5之前)為foreach 1生成的擴展形式:

我[不]理解為什么[以下]無效:

 string s; while (enumerator.MoveNext()) { s = enumerator.Current; ... 

嗯,它在語法上是有效的。 如果你在循環中所做s一切都是使用s那么一切都很好。 但是關閉s將導致反直覺的行為。 看看下面的代碼:

var countingActions = new List<Action>();

var numbers = from n in Enumerable.Range(1, 5)
              select n.ToString(CultureInfo.InvariantCulture);

using (var enumerator = numbers.GetEnumerator())
{
    string s;

    while (enumerator.MoveNext())
    {
        s = enumerator.Current;

        Console.WriteLine("Creating an action where s == {0}", s);
        Action action = () => Console.WriteLine("s == {0}", s);

        countingActions.Add(action);
    }
}

如果您運行此代碼,您將獲得以下控制台輸出:

Creating an action where s == 1
Creating an action where s == 2
Creating an action where s == 3
Creating an action where s == 4
Creating an action where s == 5

這是你所期望的。

要查看您可能不希望的內容,請在上面的代碼后立即運行以下代碼:

foreach (var action in countingActions)
    action();

您將獲得以下控制台輸出:

s == 5
s == 5
s == 5
s == 5
s == 5

為什么? 因為我們創建了五個函數,它們完全相同:打印s的值(我們已經關閉了)。 在現實中,它們是相同的功能(“打印s ”,“打印s ”,“打印s ” ...)。

在我們使用它們的時候,它們正是我們所要求的:打印s的值。 如果你看一下s的最后一個已知值,你會發現它是5 所以我們將s == 5打印五次到控制台。

這正是我們要求的,但可能不是我們想要的。

警告的第二部分......

使用不同版本的編譯器編譯時可能會有不同的行為。

......就是這樣。 從C#5開始,編譯器生成不同的代碼,通過foreach “防止”這種情況發生

因此,以下代碼將在不同版本的編譯器下生成不同的結果:

foreach (var n in numbers)
{
    Action action = () => Console.WriteLine("n == {0}", n);
    countingActions.Add(action);
}

因此,它也會產生R#警告:)

我上面的第一個代碼片段將在編譯器的所有版本中表現出相同的行為,因為我沒有使用foreach (相反,我已經將它擴展到了前C#5編譯器的方式)。

這是CLR版本嗎?

我不太清楚你在這里問的是什么。

Eric Lippert的帖子稱這種變化發生在“C#5”中。 所以 大概你必須以.NET 4.5或更高版本為目標 使用C#5或更高版本的編譯器來獲取新行為,之前的所有內容都會獲得舊行為。

但要明確的是,它是編譯器的功能,而不是.NET Framework版本。

與IL有關系嗎?

不同的代碼產生不同的IL,因此在這種意義上會產生IL的后果。

1 foreach是一個比你在評論中發布的代碼更常見的結構。 這個問題通常是通過使用foreach而不是通過手動枚舉產生的。 這就是為什么C#5中foreach的變化有助於防止這個問題,但並非完全無法解決。

第一個答案很棒,所以我想我只想補充一點。

您收到警告,因為在您的示例代碼中,reflectModel被分配了一個IEnumerable,它只會在枚舉時進行評估,如果您將reflectModel分配給范圍更廣的內容,枚舉本身可能會發生在循環之外。

如果你改變了

...Where(x => x.Name == property.Value)

...Where(x => x.Name == property.Value).ToList()

那么reflectModel將在foreach循環中被分配一個明確的列表,所以你不會收到警告,因為枚舉肯定會在循環內發生,而不是在它之外。

塊范圍的變量應解決警告。

foreach (var entry in entries)
{
   var en = entry; 
   var result = DoSomeAction(o => o.Action(en));
}

暫無
暫無

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

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