简体   繁体   English

如何在C#中的通用接口上创建协变扩展方法?

[英]How can I create a covariant extension method on a generic interface in C#?

If the title didn't make sense, here's an example: 如果标题没有意义,这是一个例子:

interface IEat { void Eat; }
class Cat : IEat { void Eat { nom(); } }
class Dog : IEat { void Eat { nom(); nom();nom(); } }
class Labrador : Dog { }

I'd like to create an extension method like this: 我想创建一个像这样的扩展方法:

public static void FeedAll(this IEnumerable<out IEat> hungryAnimals) {
   foreach(var animal in hungryAnimals) animal.Eat();
}

So I can do this: 所以我可以这样做:

listOfCats.FeedAll();
listOfLabs.FeedAll();
listOfMixedHungryAnimals.FeedAll();

Is this possible? 这可能吗? Where did I go wrong? 我哪里做错了?

The real-world application here is that "Dog" is a major base class in my application, and there are many subclasses, each of which may have ILists of things from time to time that need to have group operations performed on them. 这里真实的应用程序是“Dog”是我的应用程序中的主要基类,并且有许多子类,每个子类可能不时有IList,需要对它们执行组操作。 Having to cast them just to call an extension method on a List of an interface they all implement would be suboptimal. 必须将它们转换为在它们全部实现的接口List上调用扩展方法将是次优的。

Edit: 编辑:

I goofed up the example a little. 我稍微讨论了这个例子。 My actual code is using IList , I was hoping to have Count and index-based operations available. 我的实际代码是使用IList ,我希望可以使用Count和基于索引的操作。 Based on the answers below, I guess I'll have to go another direction for the methods that require IList semantics. 基于下面的答案,我想我将不得不为需要IList语义的方法寻找另一个方向。

This will work if you remove the out : 这将删除out

public static void FeedAll(this IEnumerable<IEat> hungryAnimals) {
   foreach(var animal in hungryAnimals) animal.Eat();
}

variance applies to parameters of the the interface itself ( T in IEnumerable<T> in this case) so a List<Dog> is compatible with IEnumerable<IEat> . 方差应用于接口本身(的参数TIEnumerable<T>在这种情况下),这样一个List<Dog>是兼容IEnumerable<IEat>

IEnumerable is already covariant, so your extension method can accept an IEnumerable<IEat> and an IEnumerable<Dog> instance will be a valid argument, making the extension method apply to variables of those types. IEnumerable已经是协变的,因此您的扩展方法可以接受IEnumerable<IEat> ,并且IEnumerable<Dog>实例将是一个有效的参数,使得扩展方法适用于这些类型的变量。

Had the definition of the interface not specified that the generic argument was covariant/contravariant then there would be nothing that you extension method could do to allow that argument to be covariant. 如果接口的定义没有指定泛型参数是协变/逆变,那么扩展方法可以做的任何事情都不允许该参数是协变的。 If you were using, say, a List which is invariant, there is nothing your extension method can do to allow for the use of covariance. 如果您使用的是一个不变的List ,那么您的扩展方法无法允许使用协方差。

As Servy notes, Enumerable<T> is already covariant, but it doesn't need to be. 正如Servy所说, Enumerable<T>已经是协变的,但它并不需要。 For older versions where the interface isn't covariant, you could do this: 对于界面不协变的旧版本,您可以这样做:

public static void FeedAll<T>(this IEnumerable<T> hungryAnimals) where T : IEat
{
    foreach(var animal in hungryAnimals) animal.Eat();
}

你不需要它通过引用传递它所以它将解决它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM