C# 嵌套泛型在使用约束时区别对待

标签 c# generics

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

例子:

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 的语法更直观。

我最初将此作为一个问题的答案发布,该问题询问使用通用约束和直接使用接口(interface)之间的区别,但我很好奇为什么会发生这种情况?

最佳答案

扩展我的评论:

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

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

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

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

Thank you for the answer; however, the constraints version could have the same code inside and would have the same issue, wouldn't it?

不!约束版本内部可能具有相同的代码,因为您无法从 EvilVertex 进行转换至 V , 也不来自 EvilEdgeE .您可以强制从类型强制转换为类型参数,首先转换为 object ,但这当然会在运行时失败。

Also, why is variance controlled at that level?

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

Your dict.Add should have the compilation error not the extension method in my view.

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

关于C# 嵌套泛型在使用约束时区别对待,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10181289/

相关文章:

c# - .NET TimeZone.CurrentTimeZone.GetDaylightChanges 返回错误的 2005 夏令时

c# - Entity Framework 包含()不工作

c# - 检查 C# 中是否安装了 Powershell 模块

swift - 从 Swift 中协议(protocol)定义的函数返回泛型

generics - 什么是参数多态函数?

ios - swift "Specialized"崩溃

c# - Entity Framework 中的多对多关系 - 结果为 “An entity object cannot be referenced by multiple instances of IEntityChangeTracker”

c# - 用于创建包含多个空白字符的字符串的 ""的替代方法

java - 实例化对象名称并设置其值的通用方法

c# - 使用 LINQ 进行快速排序