鸭编入C#编译器

注意:不是一个关于如何在C#中实现或模拟鸭子打字的问题。

几年来,我的印象是某些C#语言特性在语言本身定义的数据结构上是压缩的(对我来说,这总是看起来像一个奇怪的鸡鸡蛋)。 例如,我的印象是, foreach循环只能用于实现IEnumerabletypes。

从那时起我就开始理解C#编译器使用鸭子打字来确定一个对象是否可以在foreach循环中使用,寻找一个GetEnumerator方法而不是IEnumerable 。 这消除了鸡蛋和鸡蛋的难题,这很有意义。

我有点困惑,为什么这不是似乎与using块和IDisposable的情况下。 编译器是否有任何特殊原因不能使用鸭子打字和查找Dispose方法? 这种不一致的原因是什么?

也许在IDisposable的背后还有其他的东西呢?

讨论为什么你有一个Dispose方法没有实现IDisposable的对象超出了这个问题的范围:)

这里没有什么特别的关于IDisposable – 但是迭代器有一些特别的地方。

在C#2之前,在foreach上使用这个鸭子types是唯一的你可以实现一个强types的迭代器,也是没有装箱迭代值types的唯一方法。 我怀疑 ,如果C#和.NET有generics开始, foreach需要 IEnumerable<T> ,而不是鸭子打字。

现在编译器在我能想到的其他几个地方使用这种鸭子打字:

  • 集合初始化器寻找一个合适的Add重载(以及types必须实现IEnumerable ,只是为了表明它确实是某种集合); 这允许灵活地添加单个项目,键/值对等
  • LINQ( Select等) – 这是LINQ如何实现其灵活性,允许针对多种types的相同的查询expression式格式,而不必改变IEnumerable<T>本身
  • C#5的awaitexpression式要求GetAwaiter返回一个具有IsCompleted / OnCompleted / GetResult的awaitertypes

在这两种情况下,都可以更轻松地将该function添加到现有的types和界面,而这些概念在之前不存在。

鉴于IDisposable从第一个版本开始就已经在框架中,我不认为用鸭子inputusing语句会有什么好处。 我知道你明确地试图把讨论中没有实现IDisposable Dispose的原因打折扣,但我认为这是一个关键点。 需要有足够的理由在语言中实现一个function,我认为鸭子打字是支持一个已知接口的一个function。 如果这样做没有明确的好处,那么最终不会以语言结束。

没有鸡和蛋: foreach可以依靠IEnumerable因为IEnumerable不依赖于foreach 。 对于不执行IEnumerable集合,允许使用foreach的原因大概是历史性的 :

在C#中,为了与foreach兼容,集合类从IEnumerable和IEnumeratorinheritance并不是必须的。 只要该类具有所需的GetEnumerator,MoveNext,Reset和Current成员,它将使用foreach。 省略接口的优点是允许您定义Current的返回types比对象更具体,从而提供types安全性。

此外,并非所有的鸡和鸡蛋问题实际上都是问题:例如一个函数可以自己调用(recursion!),或者一个引用types可以包含自身(如链表)。

所以在using时,他们为什么会用棘手的方式来指定鸭子打字时,他们可以简单地说:执行IDisposable ? 从根本上说,通过使用鸭式input,你可以在types系统的末尾运行,这只有在types系统不足以解决问题时才有用。

你所问的问题不是鸡和鸡蛋的情况。 它更像是如何实现语言编译器。 就像C#和VB.NET编译器的实现是不同的。如果你编写一个hello world的简单代码,并用编译器编译它并检查IL代码,它们将会不同。 回到你的问题,我想解释一下IE代码是由C#编译器为IEnumerable生成的。

 IEnumerator e = arr.GetEnumerator(); while(e.MoveNext()) { e.Currrent; } 

所以C#编译器针对foreach的情况进行了调整。

Interesting Posts