【问题标题】:Generics in C# with multiple generic types leads to allowed and disallowed ambiguityC# 中具有多个泛型类型的泛型导致允许和不允许的歧义
【发布时间】:2012-12-05 08:26:02
【问题描述】:

我最近写了这个,很惊讶它可以编译:

public class MyGeneric<U, V> {
  MyGeneric(U u) { ... }
  MyGeneric(V v) { ... }
  public void Add(U u, V v) { ... }
  public void Add(V v, U u) { ... }
}

如果我如下使用这个类,如果我调用 Add,我会得到一个“不明确的构造函数引用”和一个“不明确的调用”。

var myVar = new MyGeneric<int, int>(new MyIntComparer());

显然,当我使用 int 和 double 作为泛型类型时没有歧义,当然,当我同时使用这两个 int 时,它们也会分配给 double。

var myVar = new MyGeneric<int, double>(new MyIntComparer());
myVar.Add(3, 5);

然后我认为以下也是允许的,但令人惊讶的是我得到了一个错误。为什么下面的不能编译?

public interface IMyInterface<T, S> {
  void Add(T t, S s);
}

public class MyGeneric<U, V> : IMyInterface<U, V>, IMyInterface<V, U> {
  public MyGeneric(U u) { }
  public MyGeneric(V v) { }
  void IMyInterface<U, V>.Add(U u, V v) { ... }
  void IMyInterface<V, U>.Add(V v, U u) { ... }
}

无论我使用隐式还是显式接口实现,编译器都会声明

'MyGeneric' 不能同时实现 'IMyInterface' 和 'IMyInterface' 因为它们可能会统一用于某些类型参数替换

为什么第一个允许写?

【问题讨论】:

  • 虽然 these two 博客文章正在讨论泛型方法和非泛型方法可能最终对某些类型参数具有相同签名的情况,但它们也可能适用于您的两种类型的论证案例。 (关于为什么允许这样做的答案基本上是“我们在 C# 2.0 中允许它,现在改变它为时已晚”。)
  • 感谢这些链接,这是我正在寻找的编译器的实现解释
  • @Rawling,感谢您提供有趣的链接。

标签: c# generics ambiguity


【解决方案1】:

1- 为什么下面的不能编译?

部分回复在该帖子中:Why does the C# compiler complain that "types may unify" when they derive from different base classes?

C# 4 规范的第 13.4.2 节规定:

由泛型类型声明实现的接口必须保留 对于所有可能的构造类型都是唯一的。如果没有这个规则,它会 不可能确定正确的调用方法 构造类型。

2-为什么第一个允许写?

编译器在编译时执行泛型类型检查,C# 4 规范的第 7.4.3.5 节规定:

虽然声明的签名必须是唯一的,但也有可能 类型参数的替换导致相同的签名。在 在这种情况下,上述重载决议的平局规则将 选择最具体的成员。以下示例显示重载 根据此规则有效和无效:

interface I1<T> {...}
interface I2<T> {...}
class G1<U>
{
    int F1(U u);                    // Overload resulotion for G<int>.F1
    int F1(int i);                  // will pick non-generic
    void F2(I1<U> a);               // Valid overload
    void F2(I2<U> a);
}
class G2<U,V>
{
    void F3(U u, V v);          // Valid, but overload resolution for
    void F3(V v, U u);          // G2<int,int>.F3 will fail
    void F4(U u, I1<V> v);      // Valid, but overload resolution for   
   void F4(I1<V> v, U u);       // G2<I1<int>,int>.F4 will fail
    void F5(U u1, I1<V> v2);    // Valid overload
    void F5(V v1, U u2);
    void F6(ref U u);               // valid overload
    void F6(out V v);
}

【讨论】:

  • 感谢您的详细解释。确实,创建 MyGeneric 基本上会两次实现相同的接口,这就是它抱怨参数替换的原因。
【解决方案2】:

这是语言规范的一部分,如此处接受的答案中所述:

Why does the C# compiler complain that "types may unify" when they derive from different base classes?

C# 4 规范的第 13.4.2 节规定:

如果从 C 创建的任何可能的构造类型在将类型参数替换为 L 后会导致 L 中的两个接口相同,则 C 的声明无效。在确定所有可能的构造类型时,不考虑约束声明。

我猜您的两个示例之间的区别在于,第二个使用接口(根据语言规范检查重复项),但第一个使用类型(未检查重复项,尽管可能会导致歧义,如您所见)。

【讨论】:

  • 谢谢,是的,没错,这基本上会从同一个界面派生两次。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-03
相关资源
最近更新 更多