问题描述
我试图做一些代码更易读。例如:的foreach(表中的一行VAR){...}
,而不是的foreach(DataRow的行table.Rows){...}
要做到这一点,我创建一个扩展方法:
命名空间System.Data这{
公共静态类MyExtensions {
公共静态的IEnumerable<&的DataRow GT;的GetEnumerator(此数据表TBL){
的foreach(在tbl.Rows的DataRow r)的收益率回报 - [R;
}
}
}
但是,编译器仍然抛出 foreach语句无法在类型System.Data.DataTable变量操作,因为System.Data.DataTable'不包含'的GetEnumerator'
。
要确认我适当地实现扩展方法我试过下面的代码,而不是与编译器没有问题的。
为(IEnumerator的<&的DataRow GT; ENM = data.GetEnumerator(); enm.MoveNext();){
无功行= enm.Current;
:
}
在你说,这是因为的IEnumerator
或的IEnumerator< DataRow的>
未实现,考虑以下不编译:
公共类的测试{
公共无效TestMethod的(){
的foreach(VAR在新MYLIST(1我,'A',这一点)) {}
}
}
公共类MYLIST {
私有对象[] _list;
公共MYLIST(params对象[]列表){_list =清单; }
公众的IEnumerator<对象>的GetEnumerator(){的foreach(VAR o在_list)收益率的回报O; }
}
有足够的困惑在目前为止其他答案。 (虽然普雷斯顿GUILLOT的答案是相当不错的,它实际上并没有把怎么在这里上的手指。)让我试图澄清。
首页关闭,你只是出于运气。 C#要求在foreach语句中使用的系列:
- 公开实施
的GetEnumerator
所要求的模式匹配 - 实施
的IEnumerable
(当然,的IEnumerable< T>
要求的IEnumerable
) - 是动态的,在这种情况下,我们只是踢可以在路上,做分析在运行时。
其结果是,集合类型必须的实际执行的的的GetEnumerator
的一种方式或其他。提供了一个扩展方法不剪。
这是不幸的。在我看来,当C#团队的补充扩展方法,C#3,他们应该修改现有的功能,如的foreach
(甚至使用
!)考虑扩展方法。但是,日程安排是C#3的发布周期,并没有获得LINQ实施时间很可能会被削减任何额外的工作项目时非常紧张。我不记得正是设计团队在这一点上说,我没有我的笔记了。
这不幸的情况是一个事实,即语言的发展和演变的结果;旧版本是专为他们的时间需求,而新版本必须建立在这个基础上。如果反事实,C#1.0已经有了扩展方法和泛型那么的foreach
循环可以被设计像LINQ:作为一个简单的句法转换。但事实并非如此,现在我们被卡住的前通用,前期推广法设计的遗产。
二,有似乎是在其他的答案和有关恰恰需要什么来使的foreach
工作的意见的一些误传。你是不是需要实现的IEnumerable
。有关此常见的误解功能的更多详细信息,请参阅。
第三,似乎有一些问题,就是是否这种行为被规范实际上有道理的。它是。该规范并没有明确叫出了扩展方法未在这种情况下,这是不幸的考虑。但是,该规范是对发生的事情非常清楚:
编译器首先做的成员查找为的GetEnumerator
。成员查找算法记录了详细的7.3节,和成员查找不考虑扩展方法,只有真实会员。扩展方法仅考虑的后经常重载解析失败的,我们还没有得到过载的分辨率呢。 (是的,扩展方法被认为的成员访问的,但是的成员访问的和的成员查找的是不同的操作。)
如果成员查找未能找到的方法组的那么尝试匹配的模式失败。编译器,因此永远不会到算法中的重载部分,因此从未有机会考虑扩展方法。
因此,您所描述的行为是一致的指定的行为。
我建议你阅读说明书的第8.8.4非常谨慎的,如果你想了解编译器如何精确分析了的foreach
语句。
第四,我鼓励你花你的时间增值你以其他方式程序。
的foreach的诱人的好处(表VAR行)
在
的foreach(在table.Rows VAR行)
很小的开发商和无形给客户。花时间增加新功能或修复错误或分析性能,而不是让已经非常清楚代码五个字符短。
I'm trying to make some code more readable. For Example foreach(var row in table) {...}
rather than foreach(DataRow row in table.Rows) {...}
.
To do this I created an extension method:
namespace System.Data {
public static class MyExtensions {
public static IEnumerable<DataRow> GetEnumerator( this DataTable tbl ) {
foreach ( DataRow r in tbl.Rows ) yield return r;
}
}
}
But the compiler still throws foreach statement cannot operate on variables of type 'System.Data.DataTable' because 'System.Data.DataTable' does not contain a public definition for 'GetEnumerator'
.
To confirm that I implemented the extension method appropriately I tried the following code instead and the compiler had no problem with it.
for ( IEnumerator<DataRow> enm = data.GetEnumerator(); enm.MoveNext(); ) {
var row = enm.Current;
...
}
Before you say that it is because IEnumerator
or IEnumerator<DataRow>
is not implemented, consider that the following does compile:
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; }
}
There is plenty of confusion in the other answers so far. (Though Preston Guillot's answer is pretty good, it does not actually put a finger on what's going on here.) Let me try to clarify.
First off, you are simply out of luck. C# requires that the collection used in a foreach statement either:
- Implement a public
GetEnumerator
that matches the required pattern. - Implement
IEnumerable
(and of course,IEnumerable<T>
requiresIEnumerable
) - Be dynamic, in which case we simply kick the can down the road and do the analysis at runtime.
The upshot is that the collection type must actually implement the GetEnumerator
one way or the other. Providing an extension method does not cut it.
This is unfortunate. In my opinion, when the C# team added extension methods to C# 3 they should have modified existing features such as foreach
(and perhaps even using
!) to consider extension methods. However, the schedule was extremely tight during the C# 3 release cycle and any extra work items that did not get LINQ implemented on time were likely to be cut. I do not recall precisely what the design team said on this point and I don't have my notes anymore.
This unfortunate situation is the result of the fact that languages grow and evolve; old versions are designed for the needs of their time, and new versions have to build on that foundation. If, counterfactually, C# 1.0 had had extension methods and generics then the foreach
loop could have been designed like LINQ: as a simple syntactic transformation. But it was not, and now we are stuck with the legacy of pre-generic, pre-extension-method design.
Second, there seems to be some misinformation in other answers and comments about what precisely is required to make foreach
work. You are not required to implement IEnumerable
. For more details on this commonly misunderstood feature, see my article on the subject.
Third, there seems to be some question as to whether this behaviour is actually justified by the specification. It is. The specification does not explicitly call out that extension methods are not considered in this case, which is unfortunate. However, the specification is extremely clear on what happens:
The compiler begins by doing a member lookup for GetEnumerator
. The member lookup algorithm is documented in detail in section 7.3, and member lookup does not consider extension methods, only actual members. Extension methods are only considered after regular overload resolution has failed, and we haven't gotten to overload resolution yet. (And yes, extension methods are considered by member access, but member access and member lookup are different operations.)
If member lookup fails to find a method group then the attempt to match the pattern fails. The compiler therefore never goes on to the overload resolution portion of the algorithm, and therefore never has a chance to consider extension methods.
Therefore the behaviour you describe is consistent with the specified behaviour.
I advise you to read section 8.8.4 of the specification very carefully if you want to understand precisely how a compiler analyzes a foreach
statement.
Fourth, I encourage you to spend your time adding value to your program in some other way. The compelling benefit of
foreach (var row in table)
over
foreach(var row in table.Rows)
is tiny for the developer and invisible to the customer. Spend your time adding new features or fixing bugs or analyzing performance, rather than making already perfectly clear code five characters shorter.
这篇关于为什么失败的foreach找到我的GetEnumerator扩展方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!