2016-07-25 6 views
4

Ich versuche zu verstehen, wie Einschränkungen (auf Klasse oder generische Methoden) die Methoden selbst beeinflussen können. Nehmen Sie diesen Code zum Beispiel:Constraints für überschriebene generische Methoden

class Base<T> where T:class, IComparable<T> 
{ 
    public virtual void Method(T obj) { } 
} 

class Derived<T> : Base<T> where T:class, IComparable<T>, IEnumerable<T> 
{ 
    public override void Method(T obj) { } 
} 

Dieser Code kompiliert fein und die Compiler/Laufzeit ist in der Lage polymorphe Anrufe an die nicht-generische Methode ‚Methode‘ zu lösen, die einen Parameter annimmt, die generischen Typ ist. In der Basisklasse und der abgeleiteten Klasse sind die Einschränkungen für den Parameter type unterschiedlich.

Ich habe auch eine Klassenbeschränkung angegeben, um Werttypen auszuschließen, die möglicherweise ein Problem verursachen, da für jede Instanziierung vom Werttyp eine andere Klasse generiert wird, während nur eine solche Klasse für Referenztypen instanziiert wird.

Auf der anderen Seite kompiliert der folgende Code nicht.

class Base 
{ 
    public virtual void Method<T>() where T : class, IComparable<T> { } 
} 

class Derived : Base 
{ 
    public override void Method<T>() where T : class, IComparable<T>, IEnumerable<T> { } 
} 

Die Sprachspezifikation für C# sagt, dass die Beschränkungen für eine generische Methode, werden wie es an eine übergeordnete Methode ist und dass es illegal ist keine Beschränkungen als solche zu spezifizieren. Ich habe bei Google nach etwas gesucht und herausgefunden, dass dies aufgrund der Komplexität, die mit der Unterstützung von polymorphen Aufrufen verbunden ist (im Hinblick auf die Pflege einer Methodentabelle usw.), nicht erlaubt ist. Aber ich verstehe immer noch nicht, warum es im obigen Fall 1 ähnlich ist, außer dass es die Klasse ist, die generisch ist. Wie kann der Compiler/Runtime dies in Fall 1 tun, während Fall 2 als Compilerfehler gekennzeichnet ist?

+0

Fall 1 Sie haben Basisklasse und dervied beide Generika –

+0

@EhsanSajjad - so? Im ersten Fall ist es die Klasse, die generisch ist, während es im zweiten Fall die Methode ist. Wenn Sie keine Klassenbeschränkung angeben, beschränken Sie die Instanziierungen auf nur eine? also warum ist man nicht erlaubt, während der andere gut funktioniert? – Madhusudhan

Antwort

3

Betrachten wir einfach die Frage "Warum ist es illegal, eine Beschränkung für eine generische Methode in einer virtuellen Überschreibung hinzuzufügen?" Weil das wirklich unkompliziert ist.

class Foo : IComparable<Foo> { ... } 
... 
Base b = new Derived(); 
b.Method<Foo>(); 

Zunächst, sollte dies illegal sein? Ja. Der Aufruf b.Method<Foo> ruft tatsächlich Derived.Method<Foo> auf, aber Foo erfüllt die Einschränkungen nicht. Das muss also illegal sein.

Auf welcher Codezeile sollte der Fehler gemeldet werden? Die Erklärung von Foo ist legal. Die Umwandlung von Derived in Base ist zulässig. Aus Sicht des Compilers ist der Aufruf an b.Method<Foo> zulässig; b ist vom Typ Base und Foo erfüllt die Einschränkungen für Base.Method. Daher kann der Fehler in keiner dieser Zeilen gemeldet werden. Der einzige Ort, an dem der Fehler gemeldet wird, ist in der Zeile Base.Method, in der die where-Klausel das Problem verursacht. Daher muss diese Art von where Klausel illegal sein, um zu verhindern, dass jemand jemals das ansonsten legale Programmfragment oben schreibt.

Nun, was ist mit Ihrem ersten Fall, wo die Klasse generisch ist? Nun, wie würden Sie in diesem Szenario eine Base<Foo> bekommen?Sicherlich nicht von einer Instanz von Derived<Foo>, weil Sie diesen Typ gar nicht erst herstellen können! Das entsprechende Problemprogramm ist:

Base<Foo> b = new Derived<Foo>(); 
b.Method(); 

Jetzt wo sollte der Fehler gemeldet werden? Natürlich kann es auf der Linie berichtet werden, die die Derived<Foo> erstellt! Es ist also nicht notwendig, die zusätzliche Klausel where für ungültig zu erklären.

4

In Fall 1 kann die Methode für jedes Objekt vom Typ T verwendet werden, wenn T von der Klasse bestimmt wird. Für jede bestimmte Klasse gibt es nur einen Typ T, also ist das Überschreiben einfach. Die Tatsache, dass Sie einen breiteren Satz von Typen T verwenden können, um Base <T> zu generieren, als abgeleitet <T> ist kein Problem.

In Fall 2 können die Methoden für eine beliebige Anzahl von Typen aufgerufen werden. Die Typen, die in der Klasse Abgeleitete zulässig sind, sind jedoch nur eine Teilmenge der Typen, die in der Basisklasse zulässig sind, was bedeutet, dass Sie eine "teilweise Überschreibung" haben, was die Dinge zu einem Chaos macht.