在 C# 中使用泛型理解协变和逆变的问题
Problem understanding covariance contravariance with generics in C#
我无法理解为什么以下 C# 代码无法编译。
如你所见,我有一个带有 IEnumerable<T>
参数的静态泛型方法 Something(并且 T
被限制为一个 IA
接口),这个参数不能' t 被隐式转换为 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)
行时出错:
Argument 1: cannot convert from 'System.Collections.Generic.List'
to 'System.Collections.Generic.IEnumerable'
错误消息信息不足,这是我的错。抱歉。
您遇到的问题是协变仅适用于引用类型这一事实的结果。
您现在可能在说 "but IA
is a reference type"。是的。但是你没有说T
等于IA
。你说T
是实现IA
的类型,值类型可以实现接口。因此我们不知道协方差是否有效,我们不允许它。
如果你想让协方差工作,你必须告诉编译器类型参数是一个引用类型,具有 class
约束以及 IA
接口约束。
错误消息实际上应该说转换是不可能的,因为协变需要保证引用类型,因为这是根本问题。
我只是想用一个代码示例来补充 Eric 出色的内幕回答,供那些可能不太熟悉通用约束的人使用。
像这样更改 Something
的签名:class
约束 必须先出现 。
public static IList<T> Something<T>(IEnumerable<T> foo) where T : class, IA
我无法理解为什么以下 C# 代码无法编译。
如你所见,我有一个带有 IEnumerable<T>
参数的静态泛型方法 Something(并且 T
被限制为一个 IA
接口),这个参数不能' t 被隐式转换为 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)
行时出错:
Argument 1: cannot convert from 'System.Collections.Generic.List' to 'System.Collections.Generic.IEnumerable'
错误消息信息不足,这是我的错。抱歉。
您遇到的问题是协变仅适用于引用类型这一事实的结果。
您现在可能在说 "but IA
is a reference type"。是的。但是你没有说T
等于IA
。你说T
是实现IA
的类型,值类型可以实现接口。因此我们不知道协方差是否有效,我们不允许它。
如果你想让协方差工作,你必须告诉编译器类型参数是一个引用类型,具有 class
约束以及 IA
接口约束。
错误消息实际上应该说转换是不可能的,因为协变需要保证引用类型,因为这是根本问题。
我只是想用一个代码示例来补充 Eric 出色的内幕回答,供那些可能不太熟悉通用约束的人使用。
像这样更改 Something
的签名:class
约束 必须先出现 。
public static IList<T> Something<T>(IEnumerable<T> foo) where T : class, IA