C# 为什么foreach找不到我的GetEnumerator扩展方法?
我正在努力使一些代码更具可读性。例如C# 为什么foreach找不到我的GetEnumerator扩展方法?,c#,foreach,extension-methods,C#,Foreach,Extension Methods,我正在努力使一些代码更具可读性。例如foreach(表中的var行){…}而不是foreach(表中的DataRow.Rows){…} 为此,我创建了一个扩展方法: namespace System.Data { public static class MyExtensions { public static IEnumerable<DataRow> GetEnumerator( this DataTable tbl ) { forea
foreach(表中的var行){…}
而不是foreach(表中的DataRow.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;
}
}
}
在你说这是因为<代码> 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<object> GetEnumerator() { foreach ( var o in _list ) yield return o; }
}
公共类测试{
公共void testMethod(){
foreach(新MyList中的vari(1,'a',this)){}
}
}
公共类MyList{
私有对象[]_列表;
公共MyList(参数对象[]列表){u list=list;}
公共IEnumerator GetEnumerator(){foreach(var o在_列表中)产生返回o;}
}
一些离题:如果你想写的更可读
foreach ( DataRow r in tbl.Rows ) yield return r;
作为
现在谈谈你的问题。。试试这个
public static IEnumerable<T> GetEnumerator<T>(this DataTable table)
{
return table.Rows.Cast<T>();
}
公共静态IEnumerable GetEnumerator(此数据表)
{
return table.Rows.Cast();
}
测试类中的GetEnumerator方法不是静态的,扩展方法是静态的。这也不会编译:
class test
{
}
static class x
{
public static IEnumerator<object> GetEnumerator(this test t) { return null; }
}
class Program
{
static void Main(string[] args)
{
foreach (var i in new test()) { }
}
}
类测试
{
}
静态x类
{
公共静态IEnumerator GetEnumerator(此测试t){return null;}
}
班级计划
{
静态void Main(字符串[]参数)
{
foreach(新测试()中的变量i){}
}
}
为了使foreach语法正常工作,您的类必须公开一个公共GetEnumerator实例方法
编辑:
从C#9.0开始,
GetEnumerator
您的扩展相当于:
public static IEnumerable<TDataRow> GetEnumerator<TDataRow>( this DataTable tbl ) {
foreach ( TDataRow r in tbl.Rows ) yield return r;
}
中的对象集合必须实现或 如果您非常希望启用此功能,那么可以创建一个包装器类,该类实现
IEnumerable
,并具有指向您的DataTable
。或者,您可以在新类中继承DataTable
,并实现IEnumerable
代码可读性通常是个人偏好。我个人认为你对foreach声明所做的更改可读性较差(但我相信有很多人同意你的观点)。到目前为止,其他答案中存在大量混乱。(尽管普雷斯顿·吉洛特的回答很好,但实际上并没有说明这里发生了什么。)让我试着澄清一下 首先关闭,你只是运气不好。C#要求foreach语句中使用的集合:
GetEnumerator
IEnumerable
(当然,IEnumerable
需要IEnumerable
)GetEnumerator
。提供扩展方法并不能解决这个问题
这是不幸的。在我看来,当C++团队向C 3增加了扩展方法时,他们应该修改现有的特性,如<代码> Frace(甚至可能使用< <代码> >代码>来考虑扩展方法。然而,在C#3发布周期中,日程安排非常紧凑,任何没有按时实现LINQ的额外工作项目都可能被削减。我记不清设计团队在这一点上说了什么,我也没有我的笔记了
这种不幸的情况是语言生长和进化的结果;旧版本是为他们的时间需求而设计的,新版本必须建立在这个基础之上。相反,如果C#1.0有扩展方法和泛型,那么foreach
循环可以像LINQ:一样设计为一个简单的语法转换。但事实并非如此,现在我们仍停留在预泛型、预扩展方法设计的遗留问题上
第二,在其他答案和评论中似乎有一些错误信息,这些信息是关于如何使foreach
工作的。您不需要实现IEnumerable
。有关此经常被误解的功能的更多详细信息,请参阅
第三,似乎存在一些问题,即这种行为是否符合规范的要求。它是。规范没有明确指出在这种情况下不考虑扩展方法,这是不幸的。但是,该规范非常清楚会发生什么:
编译器首先对GetEnumerator
执行成员查找。成员查找算法在第7.3节中详细记录,并且<强>成员查找不考虑扩展方法< /强>,只考虑<强>实际成员< /强>。只有在常规重载解析失败后才考虑扩展方法,我们还没有得到重载解析。(是的,成员访问会考虑扩展方法,但成员访问和成员查找是不同的操作。)
如果成员查找未能找到方法组,则匹配模式的尝试将失败。因此编译器永远不会进入算法的过载解决部分,因此永远没有机会考虑扩展方法。
因此,您描述的行为与指定的行为一致
如果您想准确理解编译器如何分析foreach
语句,我建议您仔细阅读本规范第8.8.4节
第四点,我鼓励你花时间以其他方式为你的计划增加价值。令人信服的好处
foreach (var row in table)
结束
对开发人员来说很小,对客户来说是看不见的。花时间添加新功能、修复bug或分析性能,而不是将已经非常清晰的代码缩短五个字符。在foreach语句中,编译器是l
public static IEnumerable<TDataRow> GetEnumerator<TDataRow>( this DataTable tbl ) {
foreach ( TDataRow r in tbl.Rows ) yield return r;
}
public static IEnumerable<DataRow> GetEnumerator( this DataTable tbl ) {
foreach (DataRow r in tbl.Rows ) yield return r;
}
foreach (var row in table)
foreach(var row in table.Rows)
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())
.....
using System;
using System.Collections.Generic;
public static class MyExtensions
{
// Note: ranges aren't intended to work like this. It's just an example.
public static IEnumerable<int> GetEnumerator(this Range range)
{
// .. do validation ..
for (var i = range.Start.Value; i <= range.End.Value; i++)
{
yield return i;
}
}
}
public class ExtensionGetEnumerator
{
public void Method()
{
var range = 1..2;
foreach (var i in range.GetEnumerator())
{
Console.WriteLine($"Print with explicit GetEnumerator {i}");
}
// The feature is in progress, scheduled for C# 9, the below does not compile yet
//foreach (var i in range)
//{
// Console.WriteLine($"Print with implicit GetEnumerator {i}");
//}
}
}