用 C # 中的泛型理解协方差逆变问题

我不明白为什么下面的 C # 代码不能编译。

如您所见,我有一个带有 IEnumerable<T>参数的静态泛型方法 Something (并且 T被限制为 IA接口) ,这个参数不能隐式转换为 IEnumerable<IA>

解释是什么? (我没有寻找变通方法,只是想知道为什么它不起作用)。

public interface IA { }
public interface IB : IA { }
public class CIA : IA { }
public class CIAD : CIA { }
public class CIB : IB { }
public class CIBD : CIB { }


public static class Test
{
public static IList<T> Something<T>(IEnumerable<T> foo) where T : IA
{
var bar = foo.ToList();


// All those calls are legal
Something2(new List<IA>());
Something2(new List<IB>());
Something2(new List<CIA>());
Something2(new List<CIAD>());
Something2(new List<CIB>());
Something2(new List<CIBD>());
Something2(bar.Cast<IA>());


// This call is illegal
Something2(bar);


return bar;
}


private static void Something2(IEnumerable<IA> foo)
{
}
}

我在 Something2(bar)行得到的错误:

参数1: 不能从‘ System.Collections.Generic.List’转换 到‘ System. Collections. Generic.IEnumable’

5509 次浏览

错误消息没有提供足够的信息,这是我的错。对此我很抱歉。

您遇到的问题是协方差只对引用类型起作用的结果。

你现在可能在说“但是 IA是一个引用类型”。是的,没错。但是你没有说 T 等于 IA。你说过 T工具a value type can implement an interface的一种类型。因此,我们不知道协方差是否会起作用,我们不允许它。

如果希望协方差工作,则必须告诉编译器 type 参数是具有 class约束和 IA接口约束的引用类型。

错误消息实际上应该说转换是不可能的,因为协方差需要引用类型的保证,因为这是基本问题。

我只是想为那些可能不太熟悉通用约束的人提供一个代码示例来补充 Eric 出色的内部答案。

像这样更改 Something的签名: class约束 必须放在第一位

public static IList<T> Something<T>(IEnumerable<T> foo) where T : class, IA