foreach为什么找不到我的GetEnumerator扩展方法?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了foreach为什么找不到我的GetEnumerator扩展方法?相关的知识,希望对你有一定的参考价值。

我正在尝试使一些代码更具可读性。例如foreach(var row in table) ...而不是foreach(DataRow row in table.Rows) ...

为此,我创建了一个扩展方法:

namespace System.Data 
    public static class MyExtensions 
        public static IEnumerable<DataRow> 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<DataRow> enm = data.GetEnumerator(); enm.MoveNext(); ) 
    var row = enm.Current;
    ...

在说这是因为未实现IEnumeratorIEnumerator<DataRow>之前,请考虑以下内容是否已编译:

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<object> GetEnumerator()  foreach ( var o in _list ) yield return o; 

答案

到目前为止,其他答案中有很多困惑。 (尽管Preston Guillot的回答相当不错,但实际上并没有使事情发生什么。)让我尝试澄清一下。

First关闭,您简直不走运。 C#要求在foreach语句中使用以下集合:

  1. 实施与所需模式匹配的公共GetEnumerator
  2. 实施IEnumerable(当然,IEnumerable<T>需要IEnumerable)] >>
  3. 保持动态,在这种情况下,我们只需要简单地进行操作即可在运行时进行分析。
  4. 结果是,收集类型必须实际上实现

GetEnumerator一种或另一种方式。提供扩展方法不会削减它。

这很不幸。在我看来,当C#团队向C#3添加扩展方法时,他们应该已经修改了现有功能,例如foreach(甚至using!),以考虑扩展方法。但是,在C#3发布周期中,进度非常紧张,并且未按时实施LINQ的任何其他工作项都可能会被削减。我不记得确切的设计团队在这一点上说,我已经没有我的笔记。

这种不幸的情况是语言发展壮大的结果;旧版本是为满足其时代需求而设计的,而新版本则必须在此基础上构建。实际上,如果C#1.0具有扩展方法和泛型,则foreach循环可以像LINQ:那样设计为简单的语法转换。但是事实并非如此,现在我们受制于前通用,前扩展方法的设计。

Second

,在其他答案和评论中似乎还有些误导有关使foreach正常工作所需要的内容。您不需要实现IEnumerable。有关此常被误解的功能的更多详细信息,请参见my article on the subject

第三,对于此行为是否在规范中实际上是合理的,似乎存在一些疑问。它是。规范没有明确指出在这种情况下不考虑扩展方法,这是不幸的。但是,该规范对发生的情况非常清楚:

编译器通过对GetEnumerator执行成员查找开始。成员查找算法在7.3节中详细介绍,并且成员查找不考虑扩展方法

,仅包含实际成员。仅在常规重载解析失败之后才考虑扩展方法,我们还没有达到重载解析的目的。 (是的,扩展方法由member access考虑,但是member accessmember lookup是不同的操作。)

如果成员查找未能找到方法组,则匹配模式的尝试将失败。因此,编译器永远不会继续执行算法的重载解决方案部分,因此永远也没有机会考虑扩展方法。

因此,您描述的行为与指定的行为一致。

如果您想准确地理解编译器如何分析foreach语句,则建议阅读规范的8.8.4节[[非常仔细

第四

,我鼓励您花时间以其他方式为程序增值。引人注目的好处foreach (var row in table)
结束

foreach(var row in table.Rows)

对于开发人员来说很小,对客户来说是看不见的。花时间来添加新功能或修复错误或分析性能,而不是将已经非常清晰的代码缩短五个字符。

您的测试类中的GetEnumerator方法不是静态的,扩展方法是静态的。这也不会编译:
另一答案
您的测试类中的GetEnumerator方法不是静态的,扩展方法是静态的。这也不会编译:
另一答案
一些offtopic:如果您想使其更具可读性,请写
另一答案
您的扩展名等效于:
另一答案
在foreach语句中,编译器正在寻找GetEnumerator的实例方法。因此,类型(此处为DataTable)必须实现IEnumerable。它永远不会找到您的扩展方法,因为它是静态的。您必须在foreach中编写扩展方法的名称。

namespace System.Data public static class MyExtensions public static IEnumerable<DataRow> GetEnumerator( this DataTable table ) foreach ( DataRow r in table.Rows ) yield return r; foreach(DataRow row in table.GetEnumerator()) .....

另一答案
当前通过扩展名将GetEnumerator添加到C#https://github.com/dotnet/csharplang/issues/3194的建议
另一答案
foreach中的对象集合必须实现foreachSystem.Collections.IEnumerable

如果您非常希望启用此功能,则可以创建一个包装类,该包装类实现System.Collections.IEnumerable并具有指向您的指针System.Collections.Generic.IEnumerable<T>。另外,您可以在新类中继承System.Collections.Generic.IEnumerable<T>并实现IEnumerable

以上是关于foreach为什么找不到我的GetEnumerator扩展方法?的主要内容,如果未能解决你的问题,请参考以下文章

在 foreach 循环中找不到函数

使用 foreach 进行并行处理时出错:“找不到函数“%dopar%””

找不到我的页面,有人可以帮我吗?

用<forEach>遍历list集合时,提示我找不到对象的属性

$Files 中的 PowerShell ForEach $file

c:forEach 抛出 javax.el.PropertyNotFoundException:在 java.lang.String 类型上找不到属性 'foo'