2012-07-15 50 views
12

我不明白爲什麼List<T>.ForEach()擴展方法在底層實現for循環。這打開了修改集合的可能性。正常的foreach會在這種情況下拋出異常,所以ForEach()應該以相同的方式反應?爲什麼列表<T> .ForEach()實現for循環?

如果你不管出於什麼原因都必須改變一個集合,那麼你肯定應該手動迭代for循環中的集合呢?

foreachList<T>.ForEach()之間似乎有點語義上的矛盾。

我錯過了什麼嗎?

+5

「這將打開增加收藏被修改的可能性。「 [確切地說,爲什麼它現在從.NET for Metro風格的應用程序中消失了。](http://stackoverflow.com/questions/10299458/is-the-listt-foreach-extension-method-gone/10299492#10299492)何哼。 – BoltClock 2012-07-15 15:56:03

+3

Eric Lippert(不贊成)[對'.ForEach'的評論](http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx)總是值得一讀在這方面。 – 2012-07-15 16:10:45

+0

順便說一句:http://stackoverflow.com/questions/10299458/is-the-listt-foreach-extension-method-gone – user287107 2012-07-15 16:23:44

回答

4

只有BCL團隊的成員可以肯定地告訴我們,但可能只是一個疏忽,您可以修改列表。

首先,David B的回答對我沒有意義。它是List<T>而不是C#,用於檢查您是否修改foreach循環內的列表,如果您這樣做則拋出InvalidOperationException。它與您使用的語言無關。

其次,有此警告在documentation

修改底層集合中的不支持此操作<牛逼>委託的身體,並導致不確定的行爲。

我發現BCL團隊不太可能想要像ForEach這樣的簡單方法有未定義的行爲。

第三,自.NET 4.5起, 如果委託人修改列表,則會拋出InvalidOperationException。如果一個程序依賴於舊的行爲,當它被重新編譯爲目標.NET 4.5時,該程序就是it will stop working。微軟願意接受這一重大變化的事實強烈表明,原來的行爲是無意的,不應該依賴。

以供參考,在這裏是如何實現在.NET 4.0中,直接從參考源:

public void ForEach(Action<T> action) { 
    if(action == null) { 
     ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); 
    } 
    Contract.EndContractBlock(); 

    for(int i = 0 ; i < _size; i++) { 
     action(_items[i]); 
    } 
} 

而這裏的它是如何被改變了.NET 4.5:

public void ForEach(Action<T> action) { 
    if(action == null) { 
     ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); 
    } 
    Contract.EndContractBlock(); 

    int version = _version; 

    for(int i = 0 ; i < _size; i++) { 
     if (version != _version && BinaryCompatibility.TargetsAtLeast_Desktop_V4_5) { 
      break; 
     } 
     action(_items[i]); 
    } 

    if (version != _version && BinaryCompatibility.TargetsAtLeast_Desktop_V4_5) 
     ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); 
} 
+0

可能的性能原因呢? – nawfal 2013-05-30 13:39:04

+0

@nawfal:可能不是。檢查'if(version!= _version)'的代價是不太可能測量的。 – 2013-05-30 17:51:34

5

List.ForEach由於從MSDN定義以下:

執行列表中的每個元件上的指定的操作。

這意味着,在該元件執行Action,可以潛在地改變元件,或集合本身。在這種情況下,沒有其他辦法(如果不創建費錢的克隆集合,如果有可能)提供這個,然後使用簡單的for

如果您在迭代過程中更改了集合foreach,它自然會引發異常。

+1

啊,根據定義它是正確的。我仍然覺得這有點誤導。我想這就是所有爭議的原因(以及他們從.NET地鐵中放棄它的原因,正如BoltClock指出的那樣)? – davenewza 2012-07-15 16:06:56

+2

@davenewza不,鏈接的文檔說_Modificare la raccolta sottostante nel corpo di'Action 'il delegato nonèsupportato e non [?] causa un comportamento indefinito._或者,如果因某種原因而喜歡英語:_修改下面的集合在'Action '的主體中,委託不受支持,並導致未定義的行爲。所以他們說你不應該修改'action'委託中的'List <>'。 – 2012-11-03 19:16:14

5

foreach是一個C#語言元素。它遵循C#的規則。

是一個.NET Framework方法。它通過.NET的規則進行播放,其中foreach不存在。

這是一個「語言vs框架」混淆的例子。框架方法必須以多種語言工作,並且語言(通常)具有矛盾的語義。

這種「語言vs框架」混淆的另一個例子是在.net 3和.NET 3.5之間對Enumerable.Cast的重大改變。在.NET 3中,Cast使用了C#語義。在.net 3.5中,它被改爲使用.net語義。