简体   繁体   English

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

[英]Generics in C# with multiple generic types leads to allowed and disallowed ambiguity

I recently wrote this and was surprised that it compiles: 我最近写了这篇文章并且惊讶于它编译:

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) { ... }
}

If I use this class as follows, I get an "Ambiguous constructor reference" and an "Ambiguous invocation" if I call Add. 如果我按如下方式使用这个类,如果我调用Add,我会得到一个“不明确的构造函数引用”和一个“不明确的调用”。

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

Obviously, there's no ambiguity when I use int and double as generic types, except of course when I use both ints, which would also both assign to a double. 显然,当我使用int和double作为泛型类型时,没有歧义,当然,当我同时使用两个int时,它们也都会分配给double。

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

So then I thought that the following was also allowed, but surprisingly I got an error. 所以我认为以下也是允许的,但令人惊讶的是我收到了一个错误。 Why is the following not allowed to compile? 为什么以下不允许编译?

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) { ... }
}

Regardless if I use implicit or explicit interface implementation, the compiler states that 无论我使用隐式还是显式接口实现,编译器都会说明

'MyGeneric<U,V>' cannot implement both 'IMyInterface<U,V>' and 'IMyInterface<V,U>' because they may unify for some type parameter substitutions 'MyGeneric <U,V>'无法同时实现'IMyInterface <U,V>'和'IMyInterface <V,U>',因为它们可能会统一某些类型参数替换

And why is the first allowed to write? 为什么第一个允许写?

1- Why is the following not allowed to compile ? 1-为什么以下不允许编译?

Part of the response is in that post : Why does the C# compiler complain that "types may unify" when they derive from different base classes? 部分响应在那篇文章中: 为什么C#编译器在从不同的基类派生时会抱怨“类型可能统一”?

The section 13.4.2 of the C# 4 specification states: C#4规范的第13.4.2节规定:

The interfaces implemented by a generic type declaration must remain unique for all possible constructed types. 通用类型声明实现的接口必须对所有可能的构造类型保持唯一。 Without this rule, it would be impossible to determine the correct method to call for certain constructed types. 如果没有这个规则,就不可能确定调用某些构造类型的正确方法。

2- And why is the first allowed to write? 2-为什么第一个允许写?

The compiler perform the generic type check at compile time, the section 7.4.3.5 of the C# 4 specification states: 编译器在编译时执行泛型类型检查,C#4规范的7.4.3.5节说明:

While signatures as declared must be unique, it is possible that substitution of type arguments results in identical signatures. 虽然声明的签名必须是唯一的,但是类型参数的替换可能会产生相同的签名。 In such cases, the tie-breaking rules of overload resolution above will pick the most specific member. 在这种情况下,上面的超载解决方案的打破平局规则将选择最具体的成员。 The following examples show overloads that are valid and invalid according to this rule: 以下示例显示了根据此规则有效且无效的重载:

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);
}

It's part of the language specification, as explained in the accepted answer here : 它是语言规范的一部分,如下面接受的答案中所述:

Why does the C# compiler complain that "types may unify" when they derive from different base classes? 为什么C#编译器在从不同的基类派生时会抱怨“类型可能统一”?

Section 13.4.2 of the C# 4 specification states: C#4规范的第13.4.2节规定:

If any possible constructed type created from C would, after type arguments are substituted into L, cause two interfaces in L to be identical, then the declaration of C is invalid. 如果从C创建的任何可能的构造类型将类型参数替换为L后,导致L中的两个接口相同,则C的声明无效。 Constraint declarations are not considered when determining all possible constructed types. 在确定所有可能的构造类型时,不考虑约束声明。

I guess the difference between your two example is that the second uses interfaces (checked for duplicates, per the language spec) but the first uses types (not checked for duplicates, despite potentially causing ambiguity as you have seen). 我猜你的两个例子之间的区别在于第二个使用接口(根据语言规范检查重复)但第一个使用类型(未检查重复,尽管可能会导致模糊不清)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM