65.9K
CodeProject 正在变化。 阅读更多。
Home

List(T).ForEach 或 Foreach,区别大吗?

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.31/5 (22投票s)

2010年6月14日

CPOL

2分钟阅读

viewsIcon

194167

使用 List 时,使用泛型列表的 ForEach 方法与使用普通的 foreach 循环,区别大吗?有时确实有区别!

引言

在 C# 中,有多种方法可以迭代列表,例如 `for` 循环、`foreach` 循环或 LINQ。当使用 `List(T)` 类型时,还有一个 `ForEach` 方法。但此方法并不总是与普通的 `foreach` 循环表现相同。

Using the Code

`List`(不是 `IList`)的 `ForEach` 方法会对列表中存储的每个对象执行一个操作。通常,它包含用于读取或修改列表中每个对象,或对列表本身执行针对每个对象的某些操作的代码。

修改对象本身

下面的示例使用 `ForEach` 方法遍历集合中所有存储的 `Points`。它从点的 x 坐标减去 10。最后,`Points` 将被打印到控制台。

List<Point> points = new List<Point>(){ new Point(14, 10), new Point(19, 10) };

items.ForEach(point => point.X = point.X - 10);

foreach (Point point in points)
{
   Console.WriteLine(point);
}

在这种情况下,控制台输出为 {X=14, Y=10} 和 {X=19, Y=10}。我期望 X 为 4 和 9,所以哪里错了?如果你将相同的逻辑放入普通的 `foreach` 语句中,编译器会抛出以下错误:“无法修改 'point' 的成员,因为它是一个 'foreach' 迭代变量”。如果我们定义自己的类型,代码就会按预期执行!

public class MyPoint
{
   public MyPoint(int x, int y){ X = x; Y = y; }
   public int X{ get; set; }
   public int Y{ get; set; }
}

List<MyPoint> points = new List<MyPoint>(){ new MyPoint(14, 10), new MyPoint(19, 10) };

items.ForEach(point => point.X = point.X - 10);

foreach (MyPoint point in points)
{
   Console.WriteLine(point);
}

区别在于,`Point` 是值类型(结构体),而 `MyPoint` 是引用类型。因此,当使用 `Point` 时,对象的副本被传递给方法,而不是对象本身。因此,如果传递给 `ForEach` 方法的操作更改了副本,它不会影响原始对象。

修改集合

当使用普通的 `foreach` 语句时,你不能在迭代集合时添加或删除项目。但是使用 `List.ForEach` 你可以,因此以下代码可以执行而不会出现任何错误。你期望什么结果?

public class Integer
{
    public int Value { get; set; }
    public Integer(int value) { Value = value; }
}

public void Sample()
{
    List<Integer> items = new List<Integer>() 
    { 
       new Integer(14), 
       new Integer(0), 
       new Integer(19) 
    };

    items.ForEach(item =>
    {
        if (item.Value == 0)
        {
            items.Remove(item);
        }
        item.Value = item.Value - 10;
    });

    foreach (Integer item in items)
    {
        Console.WriteLine(item.Value);
    }
}

控制台显示的结果是 4 和 19。这是一个很好的例子,说明并非所有你可以做的事情都应该去做!结果应该是 4 和 9!看起来内部使用了 `for` 循环,它向后迭代集合。

关注点

因此,`List.ForEach` 允许 `foreach` 循环中禁止的某些操作。这些操作被禁止是有原因的。因此,如果你想在泛型 `List` 中存储值类型对象,例如 `int`、`long`、`double`、`bool` 或甚至 `string`,则不应使用 `ForEach` 方法,以免出现问题。一个好的解决方案是使用 `for` 循环并通过集合的索引器访问数据。此外,应避免在 `ForEach` 方法中删除项目(如果可能的话)。

© . All rights reserved.