C# 为什么我不能从受约束的泛型集合推断接口?

C# 为什么我不能从受约束的泛型集合推断接口?,c#,generics,covariance,contravariance,C#,Generics,Covariance,Contravariance,我有一段代码是这样工作的: public IEnumerable<ICacheMember> Flubvert( IEnumerable<ICacheMember> members ) { // do some stuff to members return members; } ExecuteFlubversion<ICacheMemberSub>(cacheMember); public IEnumerab

我有一段代码是这样工作的:

public IEnumerable<ICacheMember> Flubvert( IEnumerable<ICacheMember> members ) 
   {
        // do some stuff to members
        return members;
   }
ExecuteFlubversion<ICacheMemberSub>(cacheMember);
public IEnumerable Flubvert(IEnumerable成员)
{
//对会员做些事情
返回成员;
}
然而,我不明白为什么我不能这样做:

public IEnumerable<T> ExecuteFlubversion<T>( IEnumerable<T> memberList ) where T: class,ICacheMember 
{
      return Flubvert( memberList );
}
public IEnumerable ExecuteFlubversion(IEnumerable成员列表),其中T:class,ICacheMember
{
返回Flubvert(成员列表);
}

当然,泛型上的约束应该保证
memberList
ICacheMember
类型的IEnumerable?我是否真的需要将现有(但隐式)的
ICacheMember
对象的集合转换为显式
ICacheMember
对象,然后再将它们转换回来?我可以理解,鉴于
Flubvert
的方法签名,我可能需要将它们转换回来,但我不明白为什么我必须在方法调用中转换它们。这就是我在工作代码中所做的,但它似乎完全不符合泛型的一般优雅行为,因此我想我一定是误解了这应该如何操作。

首先是
IEnumerable
(和其他泛型类型)的协方差仅当
T
是引用类型时才起作用,因此您需要:

public IEnumerable<ICacheMember> ExecuteFlubversion<T>(IEnumerable<T> memberList)
    where T: class, ICacheMember  // NOTE 'class'
{
    var flub = Flubvert(memberList);   // can you call with 'memberList'?
    return flub;                       // can you return that type?

    // depending on what 'Flubvert' does, maybe return 'IEnumerable<T>'
    // and say:
    // return (IEnumerable<T>)flub;
}
public IEnumerable ExecuteFlubversion(IEnumerable成员列表)
其中T:class,ICacheMember//NOTE'class'
{
var flub=Flubvert(memberList);//您能用“memberList”调用吗?
return flub;//您能返回该类型吗?
//根据“Flubvert”的作用,可能会返回“IEnumerable”
//然后说:
//返回(IEnumerable)flub;
}
还要注意,我更改了返回值。C#编译器无法保证从非泛型的
Flubvert
方法返回的对象比
IEnumerable

更具体,假设您有:

interface ICacheMemberSub : ICacheMember
{
  ...
}
你可以这样调用你的函数:

public IEnumerable<ICacheMember> Flubvert( IEnumerable<ICacheMember> members ) 
   {
        // do some stuff to members
        return members;
   }
ExecuteFlubversion<ICacheMemberSub>(cacheMember);
ExecuteFlubversion(cacheMember);

此函数将尝试返回类型为
IEnumerable
的对象,但该对象不一定可强制转换为
IEnumerable
,因此会出现错误。

如果不直接回答问题,能否将Flubvert的签名更改为泛型?如果将Flubvert设置为泛型,则方法代码的其余部分将保持不变,并且您仍然可以假定成员将是ICAchember的实现者

public IEnumerable<T> Flubvert<T>(IEnumerable<T> members)
    where T : class, ICacheMember
{
    // do some stuff to members
    return members;
}


public IEnumerable<T> ExecuteFlubversion<T>(IEnumerable<T> memberList)
    where T : class,ICacheMember
{
    return Flubvert(memberList);
}
public IEnumerable Flubvert(IEnumerable成员)
其中T:class,ICacheMember
{
//对会员做些事情
返回成员;
}
公共IEnumerable ExecuteFlubversion(IEnumerable成员列表)
其中T:class,ICacheMember
{
返回Flubvert(成员列表);
}

这是一个关于协方差和逆变换的问题。别担心,Jon Skeet很快就会来解释。你为什么要添加一个间接层次?你在作曲吗?Bridge?实现
ICacheMember
的每个类都是一个
ICacheMember
,但不是
ICacheMember
的每个实现都来自实现它的某个类。@dariogriffo我没有时间打牌,我正忙着去理解协方差:p我之所以添加间接,是因为Flubvertion过程(在我当前的示例中,一个从自定义查询系统构建的搜索/过滤机制)对ICacheMember接口非常感兴趣,它可以由缓存中找到的任何类型实现,所有类型都应该以相同的方式进行Flubvertable内部
Flubvert
看起来像什么?你能保证
成员
参数永远不会被赋值吗?因为这样你就可以解决你的问题(编辑)通过从
IEnumerable
IEnumerable
的简单转换解决问题,其中
T
由您的泛型方法定义。很好,我的代码中有这一点,因此我将更新问题。不会影响我所困惑的特定行为。@glenatron您的原始代码(编辑前)给出了编译时错误,因为泛型中的协方差仅适用于引用类型。新代码给出了另一个编译时错误。调用
Flubvert(memberList)
现在很好。问题在于
返回
部分。正如我指出的,C#编译器无法知道非泛型方法返回什么
IEnumerable
,只是它与
IEnumerable
兼容。例如,它实际上可能是
列表
,而不是
T的
列表
de>。这样的
列表中可能还有其他奇怪的东西。
。是的,你完全正确。正如我在问题中所说的,我知道我需要显式转换或更改返回签名,我非常感兴趣的是为什么我不能调用该方法。@glenatron你可以调用该方法。我将用两行代码编写你的泛型方法,以让它更清晰。编辑。你是对的,我可以。太好了,现在我不明白为什么一开始就有问题。我发誓,它之前肯定不起作用:/