C#中具有多个泛型类型的泛型会导致允许和不允许的歧义

C#中具有多个泛型类型的泛型会导致允许和不允许的歧义,c#,generics,ambiguity,C#,Generics,Ambiguity,我最近写了这篇文章,并惊讶地发现它汇编了: 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) { ... } } 公共类MyGeneric{ MyGeneric(U){…} MyGeneric(V){…} 公共无效添加(U,V){…} 公共无效添

我最近写了这篇文章,并惊讶地发现它汇编了:

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) { ... }
}
公共类MyGeneric{
MyGeneric(U){…}
MyGeneric(V){…}
公共无效添加(U,V){…}
公共无效添加(V,U){…}
}
如果我按如下方式使用这个类,我会得到一个“不明确的构造函数引用”,如果我调用Add,就会得到一个“不明确的调用”

var myVar = new MyGeneric<int, int>(new MyIntComparer());
var myVar=new MyGeneric(new MyIntComparer());
显然,当我使用int和double作为泛型类型时,没有任何歧义,当然,当我同时使用int和double时除外,这两个int也都会分配给double

var myVar = new MyGeneric<int, double>(new MyIntComparer());
myVar.Add(3, 5);
var myVar=new MyGeneric(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) { ... }
}
公共接口IMyInterface{
无效添加(T,S);
}
公共类MyGeneric:IMyInterface,IMyInterface{
公共MyGeneric(U){}
公共MyGeneric(V){}
void IMyInterface.Add(U,V){…}
void IMyInterface.Add(V,U){…}
}
不管我使用隐式还是显式接口实现,编译器都声明

“MyGeneric”无法同时实现“IMyInterface”和“IMyInterface”,因为它们可能会统一用于某些类型参数替换


为什么第一个允许写?

1-为什么不允许编译以下内容

部分回复在该帖子中:

C#4规范第13.4.2节规定:

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

2-为什么允许第一个写

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

虽然声明的签名必须是唯一的,但有可能 替换类型参数会导致相同的签名。在里面 在这种情况下,上述过载解决方案的领带断裂规则将失效 选择最具体的成员。下面的示例显示重载 根据本规则有效和无效的:

接口I1{…}
接口I2{…}
G1类
{
int F1(U);//G.F1的重载结果
int F1(int i);//将选择非泛型
void F2(I1 a);//有效重载
空隙F2(i2a);
}
G2类
{
void F3(U,V);//有效,但重载解析
void F3(V,U);//G2.F3将失败
void F4(U,I1 v);//有效,但重载解析
无效F4(I1V,U);//G2.F4将失败
void F5(U u1,I1 v2);//有效重载
无效F5(V v1,U u2);
void F6(ref U);//有效重载
无效F6(外V);
}

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

C#4规范第13.4.2节规定:

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


我猜您的两个示例之间的区别在于,第二个示例使用接口(根据语言规范检查重复项),而第一个示例使用类型(不检查重复项,尽管您已经看到可能会造成歧义).

虽然博客文章讨论了泛型方法和非泛型方法可能会在某些类型参数中使用相同的签名,但它们也可能适用于两种类型参数的情况。(关于为什么允许这样做的答案基本上是“我们在C#2.0中允许这样做,现在更改已经太迟了”。)感谢这些链接,这是我一直在寻找的编译器的实现说明for@Rawling,谢谢你有趣的链接。可能是重复的,谢谢你的详细解释。的确,创建MyGeneric基本上会实现相同的接口两次,这就是为什么它会抱怨参数替换。谢谢,是的,这是真的,它基本上会从相同的接口派生两次。
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);
}