3

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

例子:

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”上出错,说明它无法使用内联嵌套泛型转换为泛型形式。我个人觉得 for 的语法Test2更直观。

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

4

1 回答 1

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 的调用无法证明是安全的,因此编译器不允许这样做。

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

No! The constraints version could not have the same code inside, because you can't convert from EvilVertex to V, nor from EvilEdge to E. You could force a cast from the type to the type parameter, by casting first to object, but that would of course fail at run time.

Also, why is variance controlled at that level?

Because one purpose of generics is to prove the code's type safety at compile time.

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

As mentioned, the call to dict Add is a compiler error for the generic version. It can't be a compiler error for the interface version, because in the context of Test2, all you know is that you're converting EvilVertex to IVertex, which is perfectly legal.

于 2012-04-16T20:49:33.107 回答