2012-12-05 13 views
6

Niedawno napisałem to i był zaskoczony, że kompiluje:rodzajowych w C# z wielu typów generycznych prowadzi do dozwolone i niedozwolone dwuznaczności

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

Gdybym użyć tej klasy w następujący sposób, otrzymuję „niejednoznaczne odniesienie konstruktora” i "niejednoznaczne wywołanie", jeśli zadzwonię do Add.

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

Oczywiście, nie ma niejednoznaczność, gdy używam int i double jako typów generycznych, z wyjątkiem oczywiście kiedy używać zarówno ints, które również obie wyznaczać podwójne.

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

Tak więc pomyślałem, że następujące dopuszczenie było dozwolone, ale zaskakująco dostałem błąd. Dlaczego nie można skompilować poniższych elementów?

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

Niezależnie czy używam bezpośrednia lub pośrednia implementację interfejsu, kompilator stwierdza, że ​​

'MyGeneric < U, V >' nie może realizować zarówno 'IMyInterface < U, V >' i „IMyInterface < V, U > 'ponieważ mogą ujednolicić niektóre podstawienia parametrów pewnego typu

I dlaczego jest pierwszym allo czy chcesz napisać?

+2

Chociaż [to], (http://blogs.msdn.com/b/ericlippert/archive/2006/04/05/odious-ambiguous-overloads-part-one.aspx) [dwóch] (http: // blogs.msdn.com/b/ericlippert/archive/2006/04/06/odious-ambiguous-overloads-part-two.aspx) blogach mówimy o przypadku gdzie może skończyć ogólny sposób i metoda nierodzajową z tą samą sygnaturą dla niektórych argumentów typu, mogą one również mieć zastosowanie w przypadku argumentów dwóch typów. (Odpowiedź udzielona, ​​dlaczego jest to dozwolone jest w istocie „Pozwoliliśmy go w C# 2.0 i jest to zbyt późno, aby zmienić go teraz”). – Rawling

+0

Dzięki za te linki, to realizacja wyjaśnienie kompilator za Szukałem – AlexH

+0

@Rawling, dzięki za interesujący link. – Andreas

Odpowiedz

4

1- Dlaczego poniższe kompilacje nie są dozwolone?

Część odpowiedzi jest w tym poście: Why does the C# compiler complain that "types may unify" when they derive from different base classes?

Sekcja 13.4.2 z C# 4 stanów specyfikacji:

Interfejsy realizowane przez ogólny deklaracji typu musi pozostać wyjątkowy dla wszystkich możliwe rodzaje konstrukcji. Bez tej reguły nie byłoby możliwe ustalenie prawidłowej metody wywoływania niektórych typów zbudowanych w postaci .

2- I dlaczego można pisać po raz pierwszy?

Kompilator przeprowadzić ogólną kontrolę typu w czasie kompilacji, sekcja 7.4.3.5 z C# 4 stanów specyfikacji:

Mimo deklarowanego podpisy muszą być unikalne, możliwe jest, że zastępowanie argumentów typu skutkuje identycznymi podpisami. W takich przypadkach, zasady rozstrzygania spięć w powyższej kolejności przeciążania podniosą najbardziej konkretny element spośród następujących: .Poniższe przykłady pokazują przeciążeń , które są ważne i nieważne według tej reguły:

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

Dzięki za szczegółowe wyjaśnienie. To prawda, że ​​tworzenie MyGeneric byłoby w zasadzie wdrożenia tego samego interfejsu dwa razy, a to dlatego, że narzeka zmiana parametrów. – Andreas

1

Jest częścią specyfikacji języka, jak wyjaśniono w przyjętym odpowiedź tutaj:

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

sekcji 13.4.2 specyfikacji specyfikacji C# 4:

Jeśli możliwe typ utworzony z C, po tym jak argumenty typu zostaną zamienione na L, spowoduje, że dwa interfejsy w L będą identyczne, wtedy deklaracja C jest nieważna. Deklaracje dotyczące ograniczeń nie są brane pod uwagę przy określaniu wszystkich możliwych typów konstrukcji.

Chyba różnicę między dwoma przykład to, że drugi wykorzystuje interfejsy (sprawdzane duplikatów, za spec języka), ale najpierw używa typów (nie sprawdzonych duplikatów, mimo potencjalnie powodując niejasności Jak widać) .

+0

Dzięki, tak, to prawda, to w zasadzie pochodzi z tego samego interfejsu dwa razy. – Andreas