为什么呢?当然,我不需要用户定义的转化,因为List(T)
是IList(T)
而HashSet(T)
是IEnumerable(T)
。感谢。
无法隐式转换类型'
System.Collections.Generic.List<System.Collections.Generic.HashSet<string>>
&#39;到&#39;System.Collections.Generic.IList<System.Collections.Generic.IEnumerable<string>>
&#39;。存在显式转换(您是否错过了演员?)
class Program {
static IList<IEnumerable<string>> GetSet() {
return new List<HashSet<string>>();
}
}
答案 0 :(得分:2)
我认为我真正需要的是方差的简单解释 - 逆变和协方差 - 在C#泛型的背景下我找到了here和here:
错误信息并没有真正引导我,但现在我将总结一下:
<强>逆变强>
泛型类,当然是类的模板而不是类定义,可以使用 关键字使逆变成为逆变。逆变类允许从基类实例到派生类实例的赋值,即BorderCollie = Dog
public interface AllowAssignmentsFromBaseToDerived<in T>
<强>协方差强>
可以使用 out 关键字将通用类设为协变。协变类允许从派生类实例到基类实例的赋值,即Dog = BorderCollie
public interface AllowAssignmentsFromDerivedToBase<out T>
自C#1.0以来的数组类型和自C#2.0以来的委托类型以及自C#4.0以来的泛型类型参数都支持方差。
很高兴有更多的回应来覆盖我错过的更多积分,仍然感觉有点不明智。
从here取消更多信息:
如何创建变体通用接口并自行委派?
out关键字将类型参数标记为covariant,而in 关键字将其标记为逆变。两条最重要的规则 记住:
如果仅使用泛型类型参数,则可将其标记为协变 作为方法返回类型,并不用作一种形式方法 参数。
- 醇>
反之亦然,如果是类型,您可以将类型标记为逆变 仅用作一种形式的方法参数而不用作 方法返回类型。
interface IVariant<out R, in A>
{
// These methods satisfy the rules.
R GetR();
void SetA(A sampleArg);
R GetRSetA(A sampleArg);
// And these don’t.
// A GetA();
// void SetR(R sampleArg);
// A GetASetR(R sampleArg);
}
此外,如果扩展变体通用接口,则默认情况下它是不变的。您需要根据需要指定In或Out。
最后,由于我的解释将严重不足,请尝试Eric Lippert's blog
答案 1 :(得分:1)
因为IList<T>
是不变的。
为了说明这是一个问题,请考虑以下示例:
IList<T>
提供了Add(T object)
和IEnumerable<string>
的方法,这会与您的构造函数表达式new List<HashSet<string>>()
冲突。这意味着我可以调用您的program.GetSet()
并添加new ArrayList<string>()
,但您构建的实例将不允许它,因为它的合同只包含HashSet<string>
个实例(你会是什么)当人们询问包含GetSet()
的{{1}}的内容时返回
类型参数是双重嵌套的事实并不重要。例如,ArrayList<T>
也不是IList<Object>
的超类。
IList<FooClass>
本身不是这种情况(意味着IEnumerable<T>
也是IEnumerable<T>
),因为IEnumerable<SuperT>
的唯一功能是输出值。由于Liskov替代原则,这是允许的。
原则说,当走在一个阶级层次结构中时, 返回类型只能变得更通用(超类/接口) 并且参数类型只能变得更具体(sub 类/接口)。
C#提供了使用泛型类型声明中的IEnumerable<T>
和in
关键字来处理此原则(称为方差)的工具。
例如,如果您确定out
也是Foo<SubT>
,则可以将其定义为:
Foo<T>
在这种情况下,public class Foo<out T> {
T getResult () {
//do something
}
}
与T
有协变关系。如果类型参数仅用作输入,则可以进一步指定。例如,Foo
显然是以下定义中Bar<T>
的特例:
Bar<SubT>