在循环内部同时对索引变量进行递增操作时,如何使用foreach (...)语法?

4

在查看C#代码时,我经常会看到这样的模式:

DataType[] items = GetSomeItems();
OtherDataType[] itemProps = new OtherDataType[items.Length];

int i = 0;
foreach (DataType item in items)
{
    // Do some stuff with item, then finally
    itemProps[i] = item.Prop;
    i++;
}

for循环迭代items中的对象,同时保持计数器(i)用于迭代itemProps。个人不喜欢这个额外的i存在,我可能会做一些像这样的事情:

DataType[] items = GetSomeItems();
OtherDataType[] itemProps = new OtherDataType[items.Length];

for (int i = 0; i < items.Length; i++)
{
    // Do some stuff with items[i], then finally
    itemProps[i] = items[i].Prop;
}

也许第一种方法有些好处是我不知道的吗?这是因为大家都试图使用那个花哨的 foreach (...) 语法吗?我对你们的看法很感兴趣。

7个回答

7
如果您正在使用C# 3.0,那将更好;
OtherDataType[] itemProps = items.Select(i=>i.Prop).ToArray();

(从项目中选择项目.属性).ToArray() - kͩeͣmͮpͥ ͩ

4

如果 i 在数组范围之外,那么在循环完成后就可以使用 if 语句。如果您想计算项目的数量,而集合没有提供 .Count 或 .UBound 属性,则这可能会有用。

像您一样,我通常会使用第二种方法,我觉得更简洁。


2
在这种情况下,我不这么认为。有时候,尽管集合没有实现`this[int index]`方法,但它会实现`GetEnumerator()`方法。在后一种情况下,你别无选择。

1
一些数据结构并不适合于随机访问,但是可以非常快地进行迭代(如树、链表等)。因此,如果您需要迭代其中之一却需要计数,则注定要采用丑陋的方法...

1

从语义上讲,它们可能是等效的,但实际上,在枚举器上使用foreach循环可以使编译器有更多的优化空间。

我不记得所有的参数,但它们在推荐阅读的Effective C#中有很好的覆盖。


1

对于items中的每个DataType项,使用foreach循环可以清晰地表明您正在迭代所有项。也许这会使代码有点长,但它并不是“糟糕”的代码。对于其他for循环,您需要在括号内检查以了解此循环的用途。

这个例子的问题在于您正在同时迭代两个不同的数组,这种情况并不经常发生。因此,我们陷入了两种策略之间的困境。要么我们“稍微修改”一下所谓的fancy-foreach,要么我们回到旧的、不太受欢迎的for(int i = 0; i ...)。(当然还有其他方法)

所以,我认为这是Vim与Emacs之争再次出现在您的问题中,即For与Foreach循环之争 :) 喜欢for()的人会说这个foreach是无用的,可能会导致性能问题,并且代码很长。而喜欢foreach的人会说,如果我们可以读懂代码并轻松维护它,那么两行额外的代码并不重要。

最后,i 在第一个示例之外的范围内,在第二个示例中在范围内。原因是什么?因为如果您在foreach之外使用i,我会另外调用它。而且,对于我的意见,我更喜欢foreach的方式,因为您可以立即看到发生了什么。您也不必考虑它是<还是=。您立即知道正在迭代整个列表,但是,不幸的是,人们会忘记最后的i++ :D所以,我说Vim!


1
不要忘记,有些集合并没有实现直接访问操作符[],而需要使用IEnumerable接口进行迭代。最方便的访问方式是使用foreach()循环遍历。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接