2014-03-28 4 views
19

Ich habe in einen Compiler-Fehler ausgeführt werden, wenn, wie so eine Schnittstelle zweimal für die gleiche Klasse zu implementieren versuchen:Schnittstelle implementiert zweimal "Typen können vereinheitlichen"; Warum funktioniert diese Problemumgehung?

public class Mapper<T1, T2> : IMapper<T1, T2>, IMapper<T2, T1> 
{ 
    /* implementation for IMapper<T1, T2> here. */ 

    /* implementation for IMapper<T2, T1> here. */ 
} 

Der Fehler:

'Mapper' cannot implement both 'IMapper' and 'IMapper' because they may unify for some type parameter substitutions.

Warum diese Abhilfe Arbeit? Ich frage mich, ob ich das Problem gelöst habe oder nur den Compiler ausgetrickst habe.

public class Mapper<T1, T2> : MapperBase<T1, T2>, IMapper<T1, T2> 
{ 
    /* implementation for IMapper<T1, T2> here. */ 
} 

public class MapperBase<T1, T2> : IMapper<T2, T1> 
{ 
    /* implementation for IMapper<T2, T1> here. */ 
} 

EDIT: Ich habe MyClass aktualisiert, MyClassBase und IMyInterface-Mapper, MapperBase und IMapper eine reale Szenario darzustellen, wo dieses Problem selbst darstellen kann.

+0

kompiliert es, ohne die Methoden der Schnittstelle in MyClass zu implementieren? Wenn ja, haben Sie Ihre Antwort – Dhawalk

Antwort

21

Betrachten Sie diese Implementierung:

public class MyClass<T1, T2> : IMyInterface<T1, T2>, IMyInterface<T2, T1> 
{ 
    /* implementation for IMyInterface<T1, T2> here. */ 

    /* implementation for IMyInterface<T2, T1> here. */ 
} 

Was MyClass<int, int> nicht implementiert? Es implementiert IMyInterface<int, int> zweimal, weil und IMyInterface<T2, T1> vereinheitlichen, wenn T1 und T2 gleich sind. Deshalb ist die Implementierung sowohl als auch IMyInterface<T2, T1> in derselben Klasse nicht zulässig. Die gleiche Argumentation würde gelten, wenn Sie beispielsweise versuchen würden, IMyInterface<int, T1> und IMyInterface<T2, double> zu implementieren: Die Typausdrücke vereinheitlichen für T1 = double, T2 = int.

Betrachten Sie diese Implementierung:

public class MyClass<T1, T2> : MyClassBase<T1, T2>, IMyInterface<T1, T2> 
{ 
    /* implementation for IMyInterface<T1, T2> here. */ 
} 

public class MyClassBase<T1, T2> : IMyInterface<T2, T1> 
{ 
    /* implementation for IMyInterface<T2, T1> here. */ 
} 

Was Sie getan haben, ist legen Sie eine Priorität auf IMyInterface<T1, T2> über IMyInterface<T2, T1>. Für den Fall, dass T1 und T2 gleich sind und Sie eine Instanz von MyClass<T1, T2> haben, wird die IMyInterface<T1, T2> Implementierung ausgewählt. Wenn Sie eine Instanz von MyBaseClass<T1, T2> haben, wird die IMyInterface<T2, T1> Implementierung ausgewählt.

Hier ist ein Spielzeug-Programm, das Ihnen die Verhaltensweisen zeigt. Beachten Sie insbesondere das Verhalten von a_as_i.M(0, 1) und a_as_b.M(0, 1). Wenn Sie I<T2, T1> explizit auf B<T1, T2> implementieren würden (indem Sie den Methodennamen mit I<T2, T1>. voranstellen), wäre es unmöglich, ihn mit der Kompilierzeitsyntax aufzurufen. Reflexion wäre notwendig.

interface I<T1, T2> 
{ 
    void M(T1 x, T2 y); 
} 

class A<T1, T2> : B<T1, T2>, I<T1, T2> 
{ 
    public void M(T1 x, T2 y) 
    { 
     Console.WriteLine("A: M({0}, {1})", x, y); 
    } 
} 

class B<T1, T2> : I<T2, T1> 
{ 
    public void M(T2 x, T1 y) 
    { 
     Console.WriteLine("B: M({0}, {1})", x, y); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     //Outputs "A: M(0, 1)" 
     var a = new A<int, int>(); 
     a.M(0, 1); 

     //Outputs "B: M(0, 1)" 
     var b = new B<int, int>(); 
     b.M(0, 1); 

     //Outputs "A: M(0, 1)" because I<T1, T2> 
     //takes precedence over I<T2, T1> 
     var a_as_i = a as I<int, int>; 
     a_as_i.M(0, 1); 

     //Outputs "B: M(0, 1)" despite being called on an instance of A 
     var a_as_b = a as B<int, int>; 
     a_as_b.M(0, 1); 

     Console.ReadLine(); 
    } 
} 
+0

Ich würde die Antwort besser betrachten, wenn Sie Beispielmethoden hinzufügen, die die vom zweiten Beispiel erstellte Priorität anzeigen. – Euphoric

+1

@Euphoric Doing that now ... wird aktualisiert. –

+0

Meine Intuition sagt mir, dass alles, was Foo und Foo implementiert, die Methoden haben sollte, wenn T1 = T2 symmetrisch ist (und somit die Wahl der Methode irrelivant ist). Aber ich denke, im Allgemeinen gibt es keinen Grund, warum das * sein muss *. Dieser Fehler dient nur dazu, die Möglichkeit eines "undefinierten Verhaltens" zu vermeiden. –

1

Sie haben den Compiler nicht ausgetrickst, Sie haben es so gemacht, dass Sie keine konkurrierenden Funktionsdefinitionen haben. Angenommen, Ihre Schnittstelle hat die Funktion string Convert(T1 t1, T2 t2). Mit Ihrem ersten (illegalen) Code hätten Sie, wenn Sie MyClass<string, string> hätten, 2 Instanzen derselben Funktion. Mit Ihrer Basisklasse werden diese 2 Instanzen in MyClassBase und MyClass sein, so dass die eine in MyClass die andere ausblenden wird, anstatt damit in Konflikt zu geraten. Ob das funktioniert oder nicht, liegt an dir, nehme ich an.

0

Ich glaube, das Problem durch unability des Compilers, welche der implementierten Methoden aufzudecken verursacht wird aufgerufen werden soll, wenn einer der T1 oder T2 Arten Nachkomme eines anderen (oder ist die gleiche Art).
Stellen Sie sich vor, was es tun soll, wenn Sie die Klasse MyClass<int, int> instanziieren.

Verwandte Themen