你能指出这个 ForEach 扩展方法中的错误吗?
2011-10-07 16:06
423 查看
带返回值的 ForEach 扩展
Linq 中没有原生的 ForEach 扩展方法,我们可以很轻松的扩展一个:1 2 3 | public static void ForEach<T>(this IEnumerable<T> source, Action<T> action) { foreach (var element in source) action(element); } |
细心查看的话,你会发现 Enumerble 和 Queryable 中的每个扩展方法都是有返回值的,这样才能保证代码链链不断。
改进下,让 ForEach 返回 IEnumerable<T>,相信不少朋友会错误地写出如下的代码:
1 2 3 | public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<T> action) { foreach (var element in source) action(element); return source; } |
通过单元测试查找错误
我们来测试下这个扩展方法,先写第一个测试:1 2 3 | public class Employee { public string Name { get; set; } public decimal Bonus { get; set; } } [TestMethod()] public void ForEachTest() { var employees = new Employee[] { new Employee{ Name = "张三", Bonus = 500 }, new Employee { Name = "李四", Bonus = 800} }; var actualBonus = employees .ForEach(e => e.Bonus += 200) .First(e => e.Name == "李四") .Bonus; Assert.AreEqual(1000, actualBonus); } |
说明:在 .NET Framework 2.0 版中,Array 类实现 System.Collections.Generic.IList<T>、System.Collections.Generic.ICollection<T> 和 System.Collections.Generic.IEnumerable<T> 泛型接口。 由于实现是在运行时提供给数组的,因而对于文档生成工具不可见。 因此,泛型接口不会出现在 Array 类的声明语法中,也不会有关于只能通过将数组强制转换为泛型接口类型(显式接口实现)才可访问的接口成员的参考主题。 将某一数组强制转换为这三种接口之一时需要注意的关键一点是,添加、插入或移除元素的成员会引发 NotSupportedException。
摘自:http://msdn.microsoft.com/zh-cn/library/system.array.aspx
这个测试是可以顺利通过的,但不能说明代码是正确的,我们再来写一个测试。
Enumerable.Range 方法 可以生成整数序列,我们就调用它来作为 source 参数:
1 2 3 | [TestMethod()] public void ForEachTest2() { var nums = Enumerable.Range(1, 10); var actual = nums.ForEach(i => i *= 10).First(); Assert.AreEqual(10, actual); } |
第 4 行仅为了测试,其它情况下使用 Select(i => i*10) 更为恰当。
这次测试通不过了:
应该思考下了,自已动脑解决 胜过 他人直接告知 。
再给出最后一个测试,看了基本就能找出错误所在了:
1 2 3 | public IEnumerable<Employee> GetEmployees() { yield return new Employee {Name = "张三", Bonus = 500}; yield return new Employee {Name = "李四", Bonus = 800}; } [TestMethod()] public void ForEachTest3() { var employees = GetEmployees(); var actualBonus = employees .ForEach(e => e.Bonus += 200) .First(e => e.Name == "李四") .Bonus; Assert.AreEqual(1000, actualBonus); } |
问题所在
通过上面几个测试,应该发现 yield 的特性会让上面的 ForEach 出问题,请参看 Artech 的文章 《 从yield关键字看IEnumerable和Collection的区别 》进行更深入的了解。如果你使用的是 IQueryable<T> 问题可能出现,要考虑一些 ORM 框架的缓存情况。
正确实现
那么怎么正确实现 ForEach 扩展方法呢,我想还是留给大家来思考完成吧,一味接受可不是好的学习方式。另外,还可以实现如下签名的扩展,以方便使用:
1 | public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<T, int> action) {/*...*/ } |
有关 Linq 中为什么没有原生的的 ForEach 扩展的讨论,请参见文章:
ForEach
Why I Don't Use the ForEach Extension Method
Why is there not a ForEach extension method on the IEnumerable interface?
Suggestion - Enumerable.ForEach
相关文章推荐
- 经典的指针错误,在方法中,指针不要相互赋值,应该赋这个指针的内容。
- 统计一个文件夹下的所有文件并放入到一个txt中,并删除一些格式的文件,这个方法扩展,自己修改
- 错误 X “X1”不包含“XX2”的定义,并且找不到可接受类型为“X1”的第一个参数的扩展方法“XX2”(是否缺少 using 指令或程序集引用?)
- 写了些JS扩展,有错误请指出,希望对大家有用^^
- Unity中使用扩展方法解决foreach导致的GC
- Array js扩展方法 forEach()
- Windows2003 安装AD失败,未能为域创建GPO,出现了扩展错误 的解决方法
- 由于扩展配置问题而无法提供您请求的页面错误解决方法
- C# 001 --- 正确的删除一个控件的事件函数 --- 解决错误: “A”不包含“B”的定义,并且找不到可接受类型为“A”的第一个参数的扩展方法“B”(是否缺少 using 指令或程序集引用?)
- 这个常见的错误。大家记录下自己曾遇到的这样问题的解决方法
- "当前方法的代码已经过优化,无法计算表达式的值"的这个错误的解决方案!!!
- [转载]今天安装sql2000,老是出挂起的错误。所以找了找看见了这个方法。
- EF(Entity Framework)发生错误”正在创建模型,此时不可使用上下文“的解决办法。 正在创建模型,此时不可使用上下文。如果在 OnModelCreating 方法内使用上下文或如果多个线程同时访问同一上下文实例,可能引发此异常。请注意不保证 DbContext 的实例成员和相关类是线程安全的。 临时解决了这个问题,在Context的构造函数中,禁用了自动初始化:
- 为IEnumerable扩展一个ForEach方法
- Incompatible types:'TDBGridEh' and 'TDBGrid' 出现这个错误的解决方法
- 当在mybatis用到foreach的时候,会报这个错误Parameter '__frch_item_0' not found. Available parameters are [list]会出现的
- 合理使用“.NET 扩展方法”来简化代码(例子:空值判断,利用扩展方法实现LINQ操作符ForEach)
- android 代码混淆后出现:TypeReference constructed without actual type information这个错误的解决方法
- macOS 中使用 phpize 动态添加 PHP 扩展的错误解决方法
- 这个是看别人改的代码。但是有错误,,,找不到方法来改正错误。。。。今天编了一天的代码,脑细胞都要死完了。真心觉得计算机的工作不好做的啊,好好加油啊。。我的代码啊,不懂了!下面的有错误啊。