将嵌套集合转换为接口
本文关键字:接口 转换 集合 嵌套 | 更新日期: 2023-09-27 17:59:26
为什么?当然,我不需要用户定义的转换,因为List(T)
是IList(T)
,HashSet(T)
是IEnumerable(T)
。谢谢
无法将类型"
System.Collections.Generic.List<System.Collections.Generic.HashSet<string>>
"隐式转换为"System.Collections.Generic.IList<System.Collections.Generic.IEnumerable<string>>
"。存在显式转换(是否缺少强制转换?)
class Program {
static IList<IEnumerable<string>> GetSet() {
return new List<HashSet<string>>();
}
}
因为IList<T>
是不变的。
为了说明为什么这是一个问题,请考虑以下示例:
例如,
IList<T>
为Add(T object)
和IEnumerable<string>
提供了一个方法,这将与构造函数表达式new List<HashSet<string>>()
冲突。这意味着我可以调用您的program.GetSet()
并添加一个new ArrayList<string>()
,但您构建的实例不允许这样做,因为它有一个只包含HashSet<string>
实例的合同(当有人询问包含ArrayList<T>
的GetSet()
的内容时,您会返回什么?
typeparameter是双重嵌套的这一事实并不重要。例如,IList<Object>
也不是IList<FooClass>
的超类。
IEnumerable<T>
本身并非如此(意味着IEnumerable<T>
也是IEnumerable<SuperT>
),因为IEnumerable<T>
的唯一功能是输出值。这是允许的,因为利斯科夫替代原则。
该原则表明,当在类层次结构中向下走时返回类型只能变得更通用(超类/接口)并且参数类型只能变得更加具体(sub类/接口)。
C#通过在泛型类型声明中使用in
和out
关键字,提供了处理这一原理(称为方差)的工具。
例如,如果您确定Foo<SubT>
也是Foo<T>
,则可以将其定义为:
public class Foo<out T> {
T getResult () {
//do something
}
}
在这种情况下,CCD_ 27相对于CCD_ 28是协变的。如果类型参数仅用作输入,则可以进一步指定。例如,在以下定义中,Bar<T>
显然是Bar<SubT>
的特例:
public class Bar<in T> {
void setParameter(T parameter) {
//do something
}
}
我想我真正需要的是在C#泛型的上下文中对方差-逆方差和协方差-的简单解释,我在这里和这里都找到了:
错误信息并没有真正让我想到这一点,但现在我要总结一下:
对比
泛型类当然是类的模板,而不是类定义,可以使用中的关键字使其成为反变量。反向类允许从基类实例到派生类实例的赋值,即BorderCorlie=Dog
public interface AllowAssignmentsFromBaseToDerived<in T>
协方差
泛型类可以使用out关键字使协变。协变类允许从派生类实例到基类实例的赋值,即Dog=BorderCorlie
public interface AllowAssignmentsFromDerivedToBase<out T>
自C#1.0以来,数组类型、自C#2.0以来的委托类型以及自C#4.0以来的泛型类型参数都支持方差。
如果能有更多的回复来涵盖我错过的更多点,我会很高兴,但仍然觉得有点信息不足。
更多信息从这里获得:
我如何自己创建变体通用接口和委派
out关键字将类型参数标记为协变,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的博客