C# 嵌套泛型在使用约束时处理方式不同

本文关键字:处理 方式不 约束 嵌套 泛型 | 更新日期: 2023-09-27 18:31:29

使用嵌套泛型时,编译器在直接使用时失败,但在使用约束时可以正确编译。

例:

public static void Test1<V, E>(this Dictionary<V, E> dict)
    where V : IVertex
    where E : IEdge<V>
{}
public static void Test2(this Dictionary<IVertex, IEdge<IVertex>> dict){}

上面的两个扩展方法表面上具有相同的签名,但如果我现在尝试运行如下代码:

var dict = new Dictionary<VertexInstance, EdgeInstance>();
dict.Test1();
dict.Test2();

编译器会在"Test2"上出错,指出它无法转换为具有内联嵌套泛型的泛型形式。就我个人而言,我发现Test2的语法更直观。

我最初将其发布为一个问题的答案,该问题询问了使用通用约束和直接使用接口之间的差异,但我很好奇为什么会发生这种情况?

C# 嵌套泛型在使用约束时处理方式不同

扩展我的评论:

当然,这些扩展方法没有相同的签名。 Dictionary<IVertex, IEdge<IVertex>>Dictionary<VertexInstance, EdgeInstance>不同。@Payo是正确的;这是一个差异问题。类不能是协变的或逆变的。接口可以,但前提是它们被标记为它,并且不能为它标记 IDictionary,因为它不安全,因此更改为 IDictionary 在这里无济于事。

考虑是否允许这样做。 您的 Test2 实现可能如下所示:

public static void Test2(this Dictionary<IVertex, IEdge<IVertex>> dict)
{
    dict.Add(new EvilVertex(), new EvilEdge());
} 

EvilVertex和EvilEdge可以实现正确的接口,但不能从VertexInstance和EdgeInstance继承。 然后,调用将在运行时失败。 因此,对 Test2 的调用在证明上是不安全的,因此编译器不允许这样做。

谢谢你的回答;但是,约束版本内部可能具有相同的代码,并且会有相同的问题,不是吗?

不! 约束版本不能包含相同的代码,因为您无法从 EvilVertex 转换为 V ,也无法从 EvilEdge 转换为 E你可以强制从类型转换为类型参数,方法是先强制转换为object,但这当然会在运行时失败。

另外,为什么将差异控制在该级别?

因为泛型的一个目的是在编译时证明代码的类型安全性。

你的字典。在我看来,添加应该有编译错误而不是扩展方法。

如前所述,对dict Add的调用通用版本的编译器错误。 它不可能是接口版本的编译器错误,因为在Test2的上下文中,你所知道的只是你正在将EvilVertex转换为IVertex,这是完全合法的。