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

时间:2012-04-16 20:33:59

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

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

1 个答案:

答案 0 :(得分:4)

扩展我的评论:

这些扩展方法当然没有相同的签名。 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应该有编译错误而不是我视图中的扩展方法。

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