草庐IT

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

coder 2023-07-12 原文

我正在努力使一些代码更具可读性。例如 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 的回答非常好,但实际上并没有说明这里发生了什么。)让我试着澄清一下。

首先,您真不走运。 C# 要求 foreach 语句中使用的集合:

  1. 实现公开 GetEnumerator匹配所需的模式。
  2. 实现 IEnumerable (当然,IEnumerable<T> 需要 IEnumerable )
  3. 保持动态,在这种情况下,我们只需将 jar 头踢开并在运行时进行分析。

结果是集合类型必须实际实现 GetEnumerator一种或另一种方式。提供扩展方法并不能解决问题。

这很不幸。在我看来,当 C# 团队向 C# 3 添加扩展方法时,他们应该修改现有功能,例如 foreach。 (甚至可能是 using !)来考虑扩展方法。然而,在 C# 3 发布周期中,日程安排非常紧张,任何没有按时实现 LINQ 的额外工作项目都可能被削减。我不记得设计团队在这一点上说了什么,我也没有我的笔记了。

这种不幸的情况是语言发展和演变的结果;旧版本是为时代的需要而设计的,而新版本必须建立在那个基础上。如果与事实相反,C# 1.0 有扩展方法和泛型,那么 foreach loop 可以像 LINQ 一样设计:作为一个简单的句法转换。但事实并非如此,现在我们受困于前泛型、前扩展方法设计的遗留问题。

其次,在其他答案和评论中似乎存在一些关于制作 foreach 的确切要求的错误信息。工作。您不需要实现 IEnumerable .有关此常被误解的功能的更多详细信息,请参阅 my article on the subject .

第三,对于这种行为是否真的符合规范,似乎存在一些疑问。这是。不幸的是,规范没有明确指出在这种情况下不考虑扩展方法。但是,规范非常清楚会发生什么:

编译器首先对 GetEnumerator 进行成员查找 .成员查找算法在7.3节有详细说明,成员查找不考虑扩展方法,只考虑实际成员。扩展方法仅在常规重载解析失败后才被考虑,而我们还没有进行重载解析。 (是的,成员访问考虑了扩展方法,但是成员访问成员查找是不同的操作。)

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

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

如果您想准确理解编译器如何分析 foreach,我建议您非常仔细阅读规范的第 8.8.4 节。声明。

第四,我鼓励您花时间以其他方式为您的计划增加值(value)。引人注目的好处

foreach (var row in table)

结束

foreach(var row in table.Rows)

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

关于c# - 为什么 foreach 找不到我的 GetEnumerator 扩展方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14168143/

有关c# - 为什么 foreach 找不到我的 GetEnumerator 扩展方法?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  6. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

  7. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  8. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  9. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

  10. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

随机推荐