平時寫代碼的時候要對一個List<T>進行遍歷操作時,經常會糾結是要用foreach還是使用.ForEach方法。現在來搞清楚這二者之間的使用區別和性能上的差異。
一、使用
1.在foreach和list.ForEach中使用list.Remove()
在foreach中是不能使用list.Remove(),否則在進入下一個循環就會報異常,所以,如果有使用之后就必須break;
在.ForEach()中,要注意,在.net framework 4.5 會報異常:集合已修改;可能無法執行枚舉操作。在.net framework4 3.5 3.0 2.0這幾個版本下,可以直接使用list.Remove(),但如果在使用Remove()后下一項是不會遍歷到,會自動用下下項。如下代碼

1 List<Person> list = new List<Person>(){ 2 new Person(){ Id=1,Name="1"}, 3 new Person(){ Id=2,Name="2"}, 4 new Person(){ Id=3,Name="3"}, 5 new Person(){ Id=4,Name="4"}, 6 }; 7 list.ForEach(item => 8 { 9 Console.WriteLine(string.Format("111--Id:{0},Name:{1}", item.Id, item.Name)); 10 if (item.Id == 2) 11 { 12 list.Remove(item); 13 item.Id = item.Id * 10; 14 } 15 item.Id = item.Id * 10; 16 Console.WriteLine(string.Format("222--Id:{0},Name:{1}", item.Id, item.Name)); 17 }); 18 Console.WriteLine("------------"); 19 list.ForEach(item => Console.WriteLine(string.Format("Id:{0},Name:{1}", item.Id, item.Name)));
結果:
111--Id:1,Name:1
222--Id:10,Name:1
111--Id:2,Name:2
222--Id:200,Name:2
111--Id:4,Name:4
222--Id:40,Name:4
------------
Id:10,Name:1
Id:3,Name:3
Id:40,Name:4
就是在id=2這項remove后還會對這項進行操作,但在進入下一項是id=4,而id=3這項沒有遍歷到
所以如果想刪除id=2和id=3這兩項,結果是id=3是刪除不了,如下:

1 list.ForEach(item => 2 { 3 if (item.Id == 2 || item.Id==3) 4 { 5 list.Remove(item); 6 } 7 }); 8 list.ForEach(item => Console.WriteLine(string.Format("Id:{0},Name:{1}", item.Id, item.Name)));
結果:
Id:1,Name:1
Id:3,Name:3
Id:4,Name:4
只刪除了id=2這項,所以想刪除list中的項,最好還是用for

1 for (int i = 0; i < list.Count; i++) 2 { 3 if (list[i].Id == 2 || list[i].Id == 3) 4 { 5 list.Remove(list[i]); 6 i--; 7 } 8 }
注意刪除后,i--;
或者使用list.RemoveAll(),如下:

1 list.RemoveAll(item => { return item.Id == 2 || item.Id == 3; }); 2 list.ForEach(item => Console.WriteLine(string.Format("Id:{0},Name:{1}", item.Id, item.Name)));
所以,想刪除list中的項,最好不用使用foreach和list.ForEach,而是使用for或list.RemoveAll
2.在list.ForEach()中不能使用continue或者break
如果在遍歷到某個特殊項的時候,不用遍歷后面的項,需要break,這種情況使用foreach
3.list.ForEach()的使用
foreach的使用就不說了。
看一下msdn上對list.ForEach的參數的說明:
System.Action<T>
要對 List<T> 的每個元素執行的 Action<T> 委托。
Action<T> 是對傳遞給它的對象執行某個操作的方法的委托。當前 List<T> 的元素被分別傳遞給 Action<T> 委托。msdn上的示例代碼:

1 using System; 2 using System.Collections.Generic; 3 4 class Program 5 { 6 static void Main() 7 { 8 List<String> names = new List<String>(); 9 names.Add("Bruce"); 10 names.Add("Alfred"); 11 names.Add("Tim"); 12 names.Add("Richard"); 13 14 // Display the contents of the list using the Print method. 15 names.ForEach(Print); 16 17 // The following demonstrates the anonymous method feature of C# 18 // to display the contents of the list to the console. 19 names.ForEach(delegate(String name) 20 { 21 Console.WriteLine(name); 22 }); 23 } 24 25 private static void Print(string s) 26 { 27 Console.WriteLine(s); 28 } 29 }
所以,如果有需要用到委托方法,可以使用list.ForEach
平時使用list.ForEach一般都是用Lambda 表達式
二、性能
簡單寫了一個測試代碼,循環一千萬次,看下所用時間:

1 List<Person> list = new List<Person>(); 2 for (int i = 0; i < 10000000; i++) 3 { 4 list.Add(new Person() { Id = i, Name = i.ToString() }); 5 } 6 Stopwatch watch = new Stopwatch(); 7 watch.Start(); 8 foreach (var item in list) 9 { 10 item.Id = item.Id + 1; 11 } 12 watch.Stop(); 13 Console.WriteLine(string.Format("foreach執行時間:{0}", watch.Elapsed)); 14 watch = new Stopwatch(); 15 watch.Start(); 16 list.ForEach(item => { item.Id = item.Id + 1; }); 17 watch.Stop(); 18 Console.WriteLine(string.Format("list.ForEach執行時間:{0}", watch.Elapsed)); 19 watch = new Stopwatch(); 20 watch.Start(); 21 for (int i = 0; i < list.Count; i++) 22 { 23 list[i].Id = list[i].Id + 1; 24 } 25 watch.Stop(); 26 Console.WriteLine(string.Format("for1執行時間:{0}", watch.Elapsed)); 27 watch = new Stopwatch(); 28 watch.Start(); 29 int count = list.Count; 30 for (int i = 0; i < count; i++) 31 { 32 list[i].Id = list[i].Id + 1; 33 } 34 watch.Stop(); 35 Console.WriteLine(string.Format("for2執行時間:{0}", watch.Elapsed));
結果:
foreach執行時間:00:00:00.2760314
list.ForEach執行時間:00:00:00.2458242
for1執行時間:00:00:00.3641918
for2執行時間:00:00:00.1642685
效率最高是for2,for1和for2區別看代碼就知道了,list.ForEach會比foreach略快一點,但差別並不大,所以在使用foreach和list.ForEach上可以不考慮性能上的差異。
總結,foreach和list.ForEach在性能上差異不大,在使用上實際也不會有很多差別,只是在幾個特殊使用上會有所不同,但list.ForEach代碼看上去會更新簡潔一些。