为什么在C#中实现魔术方法?


16

在C#中,我开始看到所有这些魔术方法都弹出了,而没有界面的支持。为什么选择这个?

让我解释。

以前在C#中,如果对象实现了该IEnumerable接口,则该对象将自动被foreach循环迭代。这对我来说很有意义,因为它由一个接口备份,并且如果我要Iterator在要遍历的类中拥有自己的函数,则可以这样做,而不必担心它会神奇地意味着其他含义。

现在,显然,(不确定何时),不再需要这些接口。它只需要进行正确的命名转换即可。

另一个示例是通过使用一个确切 命名的方法来等待任何对象,该方法GetAwaiter具有一些特定的属性。

为什么不像以前那样创建一个界面IEnumerableINotifyPropertyChanged静态地备份这种“魔术”呢?

关于我在这里的意思的更多细节:

http://blog.nem.ec/2014/01/01/magic-methods-c-sharp/

魔术方法的优缺点是什么,在网上哪里可以找到有关为什么做出这些决定的任何信息?


4
如果您不想闭门造车,您应该编辑一下为什么“魔术不好”的个人观点。
DougM 2014年

2
如果您想知道为什么Anders Hejlsberg这么做,您必须问他。我只能告诉你为什么要这么做,这是为了向前兼容。扩展方法允许您“伪造”添加方法到现有类型,但是没有扩展接口。如果您需要一个用于async/ 的接口await,则该接口仅适用于.NET 4.5变得足够广泛而成为可行目标之后编写的代码……基本上就是现在。但是,将语法纯粹转换为方法调用后,我便可以await在事实之后为现有类型添加功能。
约尔格W¯¯米塔格

2
请注意,这基本上是面向对象的应用:只要对象响应适当的消息,它就被认为是正确的类型。
约尔格W¯¯米塔格

7
您的“以前”和“现在”是向后的-魔术方法已实现为foreach从头开始循环的基本功能。有从未一直为对象实施的要求IEnumerable进行foreach工作。这样做只是惯例。
Jesse C. Slicer 2014年

2
@gbulmer,这是我记得从以下地方得到这个想法的地方:blogs.msdn.com/b/ericlippert/archive/2011/06/30/…(在较小程度上,ericlippert.com / 2013/07/22 /…
Jesse C.Slicer 2014年

Answers:


16

通常,当无法创建以相同方式工作的接口时,将使用“魔术方法”。

foreach在C#1.0中首次引入(即行为肯定是没有什么最新的),那就只好用魔术方法,因为没有仿制药。这些选项基本上是:

  1. 使用非泛型IEnumerableIEnumeratorobjects一起使用,这意味着装箱值类型。由于迭代诸如ints 列表之类的东西应该非常快,并且当然不应创建很多垃圾箱值,因此这不是一个好选择。

  2. 等待泛型。这可能意味着将.Net 1.0(或至少foreach)延迟了3年以上。

  3. 使用魔术方法。

因此,他们选择了选项#3,但由于向后兼容的原因,它仍然留在我们这里,即使从.Net 2.0开始,要求IEnumerable<T>也可以使用。


集合初始值设定项在每种集合类型上看起来可能有所不同。比较List<T>

public void Add(T item)

Dictionary<TKey, TValue>

public void Add(TKey key, TValue value)

您不能有一个接口仅支持的第一种形式,List<T>而仅支持的第二种形式Dictionary<TKey, TValue>


LINQ方法通常被实现为扩展方法(因此,对于实现的所有类型,只有LINQ to Object的单个实现IEnumerable<T>),这意味着不可能使用接口。


对于await,该GetResult()方法可以返回一个void或某些类型T。同样,您不能拥有一个可以同时处理这两个接口的接口。虽然await是部分基于接口的:等待者必须实现INotifyCompletion,也可以实现ICriticalNotifyCompletion


1
您确定“他们选择了C#1.0的选项#3”吗?我的记忆是选项1。我的记忆是,C#声称比Java优越得多,因为C#编译器执行了“自动装箱”和“自动拆箱”。有人声称,C#代码做这样的工作的foreach 好于Java的部分原因是由于这一点。
gbulmer 2014年

1
foreachC#1.2中的文档(对于C#1.0,​​MSDN上没有任何内容)说,foreach“评估为实现IEnumerable的类型或声明GetEnumerator方法的类型” 中的表达式。而且我不确定您的意思是,拆箱在C#中始终是显式的。
2014年

1
@gbulmer而且2001年12月以来针对C#的ECMA规范(必须表示它适用于C#1.0)也指出(第15.8.4节):“如果类型C实现了System.IEnumerable接口,则它被称为集合类型。或通过满足以下所有标准来实施收集模式[…]”。
2014年

1
@svick 对序列的元素foreach(T x in sequence)应用显式强制转换T。因此,如果sequence是纯格式IEnumerable并且T是值类型,则它将取消装箱,而无需在代码中编写任何显式强制转换。C#较丑陋的部分之一。
CodesInChaos

@CodesInChaos“自动拆箱”听起来很笼统,但我没有意识到它引用了此特定功能。(由于我们一直在谈论,我想我应该有foreach。)
2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.