List<T>
有一个名为ForEach
的方法,它对每个元素执行传递的操作。
var names = new List<String>{ "Bruce", "Alfred", "Tim", "Richard" };
names.ForEach(p => { Console.WriteLine(p); });
但是如果names
不是List<T>
而是IList<T>
,IList<T>
没有类似ForEach
的方法,那该怎么办?还有其他选择吗?
答案 0 :(得分:41)
使用foreach
循环:
foreach (var p in names) {
Console.WriteLine(p);
}
如果实际上没有提高可读性,则没有理由在整个地方使用委托和扩展方法;与foreach
方法相比,ForEach
循环不会明确地告诉读者正在做什么。
答案 1 :(得分:20)
如果您的IList<T>
是一个数组(T[]
),那么您的Array.ForEach方法与ForEach
上的List<T>
类似。您可以为自定义IList<T>
或IEnumerable<T>
或您喜欢的任何内容创建扩展方法。
public static void ForEach<T>(this IList<T> list, Action<T> action)
{
foreach (T t in list)
action(t);
}
你必须要警惕原始集合中的对象将被修改的事实,但我猜这个命名确实暗示了这一点。
<强> -------------------------------------------- -------------------------------------------------- -------------------------------------- 强>
我更愿意打电话:
people.Where(p => p.Tenure > 5)
.Select(p => p.Nationality)
.ForEach(n => AssignCitizenShip(n);
大于
foreach (var n in people.Where(p => p.Tenure > 5).Select(p => p.Nationality))
{
AssignCitizenShip(n);
}
如果是这样,您可以在IEnumerable
上创建扩展方法。请注意,终止呼叫ForEach
执行Linq
查询。如果您不想要它,也可以使用yield
语句并返回IEnumerable<T>
来推迟它:
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> list, Action<T> action)
{
foreach (T t in list)
{
action(t);
yield return t;
}
}
这解决了副作用问题,但我个人喜欢名为ForEach
的方法来最终执行调用。
<强> -------------------------------------------- -------------------------------------------------- ------------------------------------- 强>
要解决偏好的相反观点,来自Eric Lippert的here is a better link比this。引用他:
“第一个原因是这样做违反了函数式编程 所有其他序列运算符所基于的原则。 显然,调用这种方法的唯一目的是引起副作用 效果。表达式的目的是计算值,而不是计算值 引起副作用。声明的目的是引起一方 影响。这个东西的呼叫网站看起来很像 表达式(但是,诚然,因为该方法是无效的, 表达式只能用在“语句表达式”中 上下文。)与我合作并不合适 序列运算符仅对其副作用有用。
第二个原因是,这样做会增加零代表性 语言的力量“。
Eric并没有说这是一件坏事 - 只是哲学背后的原因,默认情况下决定不在Linq
中包含该构造。如果您认为IEnumerable
上的某个函数不应对内容起作用,则不要这样做。就个人而言,我不介意,因为我很清楚它的作用。我将其视为导致集合类副作用的任何其他方法。如果我愿意的话,我也可以进入函数并调试它。这是Linq
本身的另一个。
people.Where(p => p.Tenure > 5)
.Select(p => p.Nationality)
.AsParallel()
.ForAll(n => AssignCitizenShip(n);
正如我所说,这些并没有什么不好。它只是个人喜好。 我不会将它用于嵌套的foreach
,或者它是否涉及在foreach
循环内执行多行代码,因为那是明显不可读的。但是对于简单的例子我发布了,我喜欢它。看起来干净简洁。
修改:查看性能链接btw:Why is List<T>.ForEach faster than standard foreach?
答案 2 :(得分:6)
您可以制作扩展方法并使用void List<T>.ForEach(Action<T> action)
的大多数实现。您可以在Shared Source Initiative站点下载源代码。
基本上你会结束这样的事情:
public static void ForEach<T>(this IList<T> list, Action<T> action)
{
if (list == null) throw new ArgumentNullException("null");
if (action == null) throw new ArgumentNullException("action");
for (int i = 0; i < list.Count; i++)
{
action(list[i]);
}
}
它稍微好于使用foreach
语句的其他实现,因为它利用了IList包含索引器的事实。
虽然我对O. R. Mapper的回答很有帮助,但有时候在很多开发人员的大项目中,很难让每个人都相信foreach
陈述更清楚。更糟糕的是,如果您的API基于接口(IList)而不是具体类型(List),那么习惯List<T>.ForEach method
的开发人员可能会开始在您的IList引用上调用ToList
!我知道,因为它发生在我之前的项目中。我在Framework Design Guidelines之后的公共API中使用了集合接口。我花了一段时间注意到许多不习惯这种情况并且致电ToList
的开发人员开始以惊人的速度开始使用。最后,我将此扩展方法添加到每个人都在使用的公共程序集中,并确保从代码库中删除了对ToList
的所有不必要的调用。
答案 3 :(得分:2)
将此代码添加到静态类并将其称为扩展名:
public static void ForEach<T>(this IList<T> list, Action<T> action) {
foreach(var item in list) {
action.Invoke(item);
}
}