如何在C#中的泛型接口上创建协变扩展方法

本文关键字:创建 扩展 方法 泛型接口 | 更新日期: 2023-09-27 18:22:27

如果标题没有意义,这里有一个例子:

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

我想创建一个扩展方法,如下所示:

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

所以我可以这样做:

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

这可能吗?我哪里错了?

这里的实际应用程序是,"Dog"是我的应用程序中的一个主要基类,并且有许多子类,每个子类可能不时有需要对其执行组操作的事物的ILlist。仅为了在它们都实现的接口的List上调用扩展方法而强制转换它们将是次优的。

编辑:

我把这个例子搞砸了一点。我实际的代码是使用IList,我希望可以使用Count和基于索引的操作。根据下面的答案,我想对于需要IList语义的方法,我将不得不转向另一个方向。

如何在C#中的泛型接口上创建协变扩展方法

IEnumerable已经是协变的,因此您的扩展方法可以接受IEnumerable<IEat>,并且IEnumerable<Dog>实例将是有效的参数,从而使扩展方法应用于这些类型的变量。

如果接口的定义没有指定泛型参数是协变/逆变的,那么扩展方法就无法允许该参数是协变量的。如果您使用的是不变的List,那么您的扩展方法无法允许使用协方差。

如果删除out:,这将起作用

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

方差适用于接口本身的参数(在这种情况下是IEnumerable<T>中的T),所以List<Dog>IEnumerable<IEat>是兼容的。

正如Servy所指出的,Enumerable<T>已经是协变的,但它不需要。对于接口不是协变的旧版本,您可以这样做:

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

您不需要通过引用传递out,所以它可以处理out。