鸭子在C#编译器中输入

Mat*_*vey 61 c# compiler-construction idisposable duck-typing

注意不是关于如何在C#中实现或模拟duck typing的问题...

几年来,我的印象是某些C#语言特性对语言本身定义的数据结构感到沮丧(这对我来说似乎总是一个奇怪的鸡蛋和鸡蛋场景).例如,我的印象是foreach循环只能用于实现的类型IEnumerable.

从那时起,我开始明白C#编译器使用duck typing来确定是否可以在foreach循环中使用对象,GetEnumerator而不是查找方法IEnumerable.这很有意义,因为它消除了鸡蛋和鸡蛋的难题.

我有点困惑,为什么这不是using块的情况似乎并非如此IDisposable.是否有任何特殊原因编译器不能使用duck typing并查找Dispose方法?造成这种不一致的原因是什么?

或许还有其他东西在IDisposable的引擎盖下进行?

讨论为什么你会永远有未实现IDisposable是这个问题的范围之外的Dispose方法的对象:)

Jon*_*eet 39

没有什么特别之处IDisposable这里-但有什么特别的迭代器.

在C#2之前,使用这种duck类型foreach唯一可以实现强类型迭代器的方法,也是在没有装箱的情况下迭代值类型的唯一方法.我怀疑,如果C#和.NET已经开始使用泛型,foreach那就需要了 IEnumerable<T>,而不是打字.

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

  • 集合初始化器寻找合适的Add重载(以及必须实现的类型IEnumerable,只是为了表明它确实是某种类型的集合); 这允许灵活地添加单个项目,键/值对等
  • LINQ(Select等) - 这就是LINQ实现其灵活性的方式,允许针对多种类型使用相同的查询表达式格式,而无需IEnumerable<T>自行更改
  • 的C#5等待表达式需要GetAwaiter返回其具有awaiter类型IsCompleted/ OnCompleted/GetResult

在这两种情况下,这使得更容易将功能添加到现有类型和接口,而这些概念之前并不存在.

鉴于IDisposable自第一个版本以来一直在框架中,我认为在键入using语句时没有任何好处.我知道你明确地试图忽略Dispose没有IDisposable从讨论中实现的原因,但我认为这是一个关键点.需要有充分的理由在语言中实现一个功能,我认为鸭子打字是一种超越支持已知界面的功能.如果这样做没有明显的好处,它将不会以语言结束.

  • 使用带有"for each"的duck-typing允许编译器在迭代器是结构的情况下避免装箱.虽然我认为结构实现IEnumerator(通用或非通用)是不好的,但有时避免每次循环开始时创建一个新对象都是有利的.让GetEnumerator方法返回一个struct和一个IEnumerable.GetEnumerator方法返回一个实现IEnumerator的类可能允许两全其美. (5认同)
  • 表达式创建(来自lambda)是另一个例子; 但是,任何地方都没有严格记录 (3认同)
  • 顺便说一句,值得注意的是,至少在VB中,如果GetEnumerator的返回类型是IEnumerable,编译器将在运行时*检查*以查看它是否实现了IDisposable; 如果返回类型是其他类型,编译器将在编译时*检查*,但在运行时将*不*检查.即使枚举器是实现IDisposable的派生类型,如果*声明的*类型没有实现IDisposable,编译器也不会尝试在运行时调用它. (3认同)
  • @Jon Skeet谢谢Jon的回答!编译器是否也使用带有集合初始值设定项的duck类型?(即寻找Add()方法而不是ICollection) (2认同)
  • 如果类具有不需要IDisposable的枚举器,但派生类的枚举器需要它,则会产生问题.使用IEnumerable&lt; T&gt;以外的任何其他内容 因为GetEnumerator方法的返回类型可能会阻止派生类指定新的枚举方法. (2认同)

Eam*_*nne 12

没有鸡肉和鸡蛋: foreach可能取决于IEnumerable因为IEnumerable不依赖foreach.未实施的集合允许foreach的原因IEnumerable可能主要是历史性的:

在C#中,集合类不一定要从IEnumerable和IEnumerator继承,以便与foreach兼容; 只要该类具有所需的GetEnumerator,MoveNext,Reset和Current成员,它将与foreach一起使用.省略接口的优点是允许您将Current的返回类型定义为比对象更具体,从而提供类型安全性.

此外,并非所有鸡和蛋问题都是实际问题:例如,函数可以调用自身(递归!)或引用类型可以包含自身(如链接列表).

因此,当using它们出现时为什么他们会使用一些棘手的东西来指定为鸭子打字时他们可以简单地说:实施IDisposable?从根本上说,通过使用duck类型,你可以在类型系统周围进行最终运行,这仅在类型系统不足以(或不切实际)解决问题时才有用.