为什么foreach无法找到我的GetEnumerator扩展方法?
我试图使一些代码更具可读性。 例如foreach(var row in table) {...}
而不是foreach(DataRow row in table.Rows) {...}
。
为此,我创建了一个扩展方法:
namespace System.Data { public static class MyExtensions { public static IEnumerable GetEnumerator( this DataTable tbl ) { foreach ( DataRow r in tbl.Rows ) yield return r; } } }
但是编译器仍然抛出foreach statement cannot operate on variables of type 'System.Data.DataTable' because 'System.Data.DataTable' does not contain a public definition for 'GetEnumerator'
。
为了确认我已经适当地实现了扩展方法,我尝试了以下代码,编译器没有问题。
for ( IEnumerator enm = data.GetEnumerator(); enm.MoveNext(); ) { var row = enm.Current; ... }
在您说这是因为未实现IEnumerator
或IEnumerator
,请考虑以下内容进行编译:
public class test { public void testMethod() { foreach ( var i in new MyList( 1, 'a', this ) ) { } } } public class MyList { private object[] _list; public MyList( params object[] list ) { _list = list; } public IEnumerator GetEnumerator() { foreach ( var o in _list ) yield return o; } }
到目前为止,其他答案中存在许多混淆。 (虽然Preston Guillot的答案非常好,但实际上并没有指出这里发生的事情。)让我试着澄清一下。
首先 ,你只是运气不好。 C#要求foreach语句中使用的集合:
- 实现与所需模式匹配的公共
GetEnumerator
。 - 实现
IEnumerable
(当然,IEnumerable
需要IEnumerable
) - 保持动态,在这种情况下,我们只需将jar子放在路上并在运行时进行分析。
结果是集合类型必须以某种方式实现 GetEnumerator
。 提供扩展方法不会削减它。
这很不幸。 在我看来,当C#团队向C#3添加扩展方法时,他们应该修改现有的function,例如foreach
(甚至可能using
!)来考虑扩展方法。 但是,在C#3发布周期中,时间表非常紧张,并且任何未及时实现LINQ的额外工作项都可能被削减。 我不记得设计团队在这一点上说了什么,我再也没有笔记了。
这种不幸的情况是语言成长和发展的结果; 旧版本是为他们的时间需求而设计的,新版本必须建立在这个基础之上。 如果,反事实地,C#1.0有扩展方法和generics,那么foreach
循环可以像LINQ一样设计:作为简单的句法转换。 但事实并非如此,现在我们仍然坚持预先通用的预扩展方法设计。
其次 ,在其他答案和评论中似乎存在一些错误的信息,这些答案和评论是关于使foreach
工作所需的精确内容。 您不需要实现IEnumerable
。 有关这个常被误解的function的更多详细信息,请参阅我关于此主题的文章 。
第三 ,关于这种行为是否真的被规范certificate是合理的,似乎存在一些问题。 它是。 规范没有明确地指出在这种情况下不考虑扩展方法,这是不幸的。 但是,规范非常明确:
编译器首先对GetEnumerator
进行成员查找 。 成员查找算法在7.3节中详细记录, 成员查找不考虑扩展方法 ,只考虑 实际成员 。 仅在常规重载解析失败后才考虑扩展方法,并且我们还没有超载解决方案。 (是的,扩展方法被成员访问考虑,但成员访问和成员查找是不同的操作。)
如果成员查找未能找到方法组,则匹配该模式的尝试将失败。 因此编译器永远不会继续算法的重载解析部分,因此永远不会有机会考虑扩展方法。
因此,您描述的行为与指定的行为一致。
如果您想要准确理解编译器如何分析foreach
语句,我建议您仔细阅读规范的第8.8.4节。
第四 ,我鼓励您花时间以其他方式为您的计划增加价值。 令人信服的好处
foreach (var row in table)
过度
foreach(var row in table.Rows)
对开发人员来说很小,对客户来说是不可见的。 花时间添加新function或修复错误或分析性能,而不是将已经完全清晰的代码缩短五个字符。
测试类中的GetEnumerator方法不是静态的,扩展方法是。 这不会编译:
class test { } static class x { public static IEnumerator
为了使foreach语法糖起作用,您的类必须公开一个公共GetEnumerator 实例方法。
一些offtopic:如果你想做更可读的写
foreach ( DataRow r in tbl.Rows ) yield return r;
如
foreach (DataRow row in tbl.Rows) { yield return row; }
现在你的问题..试试这个
public static IEnumerable GetEnumerator (this DataTable table) { return table.Rows.Cast (); }
您的扩展程序相当于:
public static IEnumerable GetEnumerator ( this DataTable tbl ) { foreach ( TDataRow r in tbl.Rows ) yield return r; }
GetEnumerator
与GetEnumerator
方法不同
这将更好地工作:
public static IEnumerable GetEnumerator( this DataTable tbl ) { foreach (DataRow r in tbl.Rows ) yield return r; }
在foreach语句中,编译器正在寻找GetEnumerator的实例方法。 因此类型(此处为DataTable)必须实现IEnumerable。 它永远不会找到你的扩展方法,因为它是静态的。 您必须在foreach中写下扩展方法的名称。
namespace System.Data { public static class MyExtensions { public static IEnumerable GetEnumerator( this DataTable table ) { foreach ( DataRow r in table.Rows ) yield return r; } } } foreach(DataRow row in table.GetEnumerator()) .....
为避免混淆,我建议您为扩展方法使用不同的名称。 也许像GetRows()
foreach
的对象集合必须实现System.Collections.IEnumerable
或System.Collections.Generic.IEnumerable
。
如果您非常强烈地希望启用此function,那么您可以创建一个实现IEnumerable
的包装器类,并具有指向DataTable
的指针。 或者,您可以在新类中inheritanceDataTable
并实现IEnumerable
。
代码可读性通常是个人偏好。 我个人觉得你对foreach
语句的更改不太可读(但我相信很多人都同意你的意见)。
上述就是C#学习教程:为什么foreach无法找到我的GetEnumerator扩展方法?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/960346.html