Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/284.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在C#中的泛型接口上创建协变扩展方法?_C#_Generics_Covariance - Fatal编程技术网

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

如何在C#中的泛型接口上创建协变扩展方法?,c#,generics,covariance,C#,Generics,Covariance,如果标题没有意义,下面是一个例子: 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> hungryAnim

如果标题没有意义,下面是一个例子:

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();
}
这可能吗?我哪里出错了

这里的实际应用程序是,“Dog”是我的应用程序中的一个主要基类,并且有许多子类,每个子类可能不时有需要对其执行组操作的对象的IList。必须强制转换它们,只是为了在它们都实现的接口列表上调用扩展方法,这是次优的

编辑:


我把这个例子弄错了一点。我的实际代码使用的是
IList
,我希望有基于计数和索引的操作可用。根据下面的答案,我想对于需要IList语义的方法,我必须转向另一个方向。

如果您将
移出
,这将起作用:

public static void FeedAll(this IEnumerable<IEat> hungryAnimals) {
   foreach(var animal in hungryAnimals) animal.Eat();
}
public static void FeedAll(此IEnumerable hungryAnimals){
foreach(饥饿动物中的动物变量)animal.Eat();
}

方差应用于接口本身的参数(
T
在本例中为
IEnumerable
),所以
列表与
IEnumerable
兼容,您不需要out,它通过引用传递,所以它可以计算出来。

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


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

正如Servy所指出的,
可枚举的
已经是协方差的,但不必是协方差的。对于接口不协变的旧版本,可以执行以下操作:

public static void FeedAll<T>(this IEnumerable<T> hungryAnimals) where T : IEat
{
    foreach(var animal in hungryAnimals) animal.Eat();
}
public static void FeedAll(这是IEnumerable hungryAnimals),其中T:IEat
{
foreach(饥饿动物中的动物变量)animal.Eat();
}


如果没有
out
关键字,它是否可以正常工作?一个
IEnumerable
已经是一个
IEnumerable
,因为
IEnumerable
是协变的。Strilanc,你说得对,我对我的问题做了一些编辑。我希望用IList实现这一点,显然这是不可能的(我理解为什么IList不是协变的,我只是希望有一个解决办法,特别是因为我不需要添加。)大多数列表实现
IReadOnlyList
(不幸的是它不能放在
IList
之上),所以这可能是可行的。它非常适合我使用。升级到4.5.1是我早就应该做的事情。参数不是通过引用传递的,所以这不是它工作的原因。请阅读“通过引用传递与通过值传递”一节中的链接。你指的那篇文章怎么样?我知道有什么区别。如果您认为这个方法是通过引用传递参数,那么您需要再次阅读,因为这里根本没有发生这种情况。由于不使用
out
ref
参数是按值传递的。他的错误是在签名中使用关键字out。他这样做的原因是因为他相信自己需要它。基本上这句话就是我的意思。“现在,如果将基于此类型的对象传递给方法,则会传递对该对象的引用。下面的示例将SampleRefType类型的对象传递给method ModifyObject”他使用关键字
out
的方式只是无效语法,会导致编译时错误。它不在有效位置,无法指示参数是通过引用传递的。此外,扩展方法的隐式参数不能通过引用传递;这样做是无效的。此外,从这个问题中可以很清楚地看到,他并不是试图指出参数应该通过引用传递,他是说它需要协变。最后,如果它是通过引用传递的,而它不是,这不会导致它是协变的。编辑问题以使其更清楚。@richardtallent和
IList
不是协变的,而是不变的。正如我所说的,没有任何方法可以使不变参数协变地起作用;您只需要使用协变类型作为参数。这可能会促使我转向.NET 4.5,因为IList实现了IReadOnlyList,我相信这会解决这个问题,不是吗?@richardtallent
IReadOnlyList
是协变接口,是的。当然,它的存在不会使IList变得协变。如果接口不是协变的,则不需要做任何事情来使参数协变。您可以将扩展方法改为泛型:
static void M(此参数),其中T:Dog
将执行此操作。@hangar您的意思是什么?重载解析也将处理具有构造泛型类型的重载。然而,你可能不会调用你认为你正在调用的重载;是吗?不,C#不会被泛型约束重载。“看,”机库当然可以,但我不是在说那个。我说的是构造的泛型类型,比如
这个IEnumerable
。我猜最初的评论听起来有点刺耳。如果您不需要使用不同的约束重载,那么这确实有效。
public static void FeedAll<T>(this IEnumerable<T> hungryAnimals) where T : IEat
{
    foreach(var animal in hungryAnimals) animal.Eat();
}