如何在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语义的方法,我将不得不转向另一个方向。
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。