In objektorientierten Sprachen, die generische Typparameter unterstützen (auch als Klassenvorlagen und parametrischer Polymorphismus bezeichnet), ist es häufig möglich, eine Typbeschränkung für den Typparameter anzugeben, sodass er abgeleitet wird von einem anderen Typ. Dies ist beispielsweise die Syntax in C #:
//for classes:
class ExampleClass<T> where T : I1 {
}
//for methods:
S ExampleMethod<S>(S value) where S : I2 {
...
}
Welche Gründe sprechen dafür, tatsächliche Schnittstellentypen gegenüber Typen zu verwenden, für die diese Schnittstellen gelten? Was sind zum Beispiel die Gründe für die Signatur der Methode I2 ExampleMethod(I2 value)
?
object-oriented
type-systems
generics
GregRos
quelle
quelle
ref
Werttypparametern kann der Werttyp tatsächlich geändert werden.Antworten:
Mit der parametrischen Version gibt
Nehmen wir als zufälliges Beispiel an, wir haben eine Methode, die die Wurzeln einer quadratischen Gleichung berechnet
Und dann möchten Sie, dass es auch mit anderen Arten von Zahlen wie anderen Dingen funktioniert
int
. Sie können so etwas schreibenDas Problem ist, dass dies nicht sagt, was Sie wollen. Es sagt
Wir können so etwas wie
int sol = solve(a, b, c)
ifa
, nicht machenb
undc
sindint
s, weil wir nicht wissen, dass die Methodeint
am Ende eine zurückgibt! Dies führt zu etwas umständlichem Tanzen mit Niederwerfen und Beten, wenn wir die Lösung in einem größeren Ausdruck verwenden möchten.Innerhalb der Funktion könnte uns jemand einen Float, einen Bigint und Grade geben, und wir müssten sie addieren und miteinander multiplizieren. Wir möchten dies statisch ablehnen, da die Operationen zwischen diesen drei Klassen nur Kauderwelsch sind. Grad sind mod 360, also wird es nicht so sein, dass
a.plus(b) = b.plus(a)
ähnliche Heiterkeiten auftauchen.Wenn wir den parametrischen Polymorphismus mit Subtypisierung verwenden, können wir dies alles ausschließen, da unser Typ tatsächlich sagt, was wir meinen
Oder in Worten "Wenn Sie mir einen Typ geben, der eine Zahl ist, kann ich Gleichungen mit diesen Koeffizienten lösen".
Dies kommt auch an vielen anderen Orten vor. Eine weitere gute Quelle für Beispiele sind Funktionen , die abstrakt über eine Art Container, ala
reverse
,sort
,map
etc.quelle
Num<int>
) parametrisierte Schnittstelle als zusätzliches Argument akzeptiert werden . Sie können die Schnittstelle jederzeit für jeden Typ durch Delegierung implementieren. Dies ist im Wesentlichen das, was Haskells Typklassen sind, mit der Ausnahme, dass die Verwendung sehr viel langwieriger ist, da Sie die Schnittstelle explizit weitergeben müssen.Denn genau das brauchen Sie ...
sind zwei ausgesprochen unterschiedliche Unterschriften. Der erste nimmt einen beliebigen Typ an, der die Schnittstelle implementiert, und die einzige Garantie ist, dass der Rückgabewert der Schnittstelle entspricht.
Der zweite nimmt jeden Typ an, der die Schnittstelle implementiert, und garantiert, dass er mindestens diesen Typ erneut zurückgibt (und nicht etwas, das die weniger restriktive Schnittstelle erfüllt).
Manchmal möchten Sie die schwächere Garantie. Manchmal willst du den Stärkeren.
quelle
Or
, die zweiParser
Objekte nimmt (eine abstrakte Basisklasse, aber das Prinzip gilt) und eine neue zurückgibtParser
(aber mit einem anderen Typ). Der Endverbraucher sollte den konkreten Typ nicht kennen oder sich darum kümmern.IEnumerable<T>
, gibt ein anderes zurück,IEnumerable<T>
was zB tatsächlich ein istOrderedEnumerable<T>
)Die Verwendung von eingeschränkten Generika für Methodenparameter kann es einer Methode ermöglichen, ihren Rückgabetyp genau auf den des übergebenen Objekts abzustimmen. In .NET können sie auch zusätzliche Vorteile bieten. Darunter:
Einer Methode, die ein eingeschränktes generisches Element als Parameter
ref
oder akzeptiert,out
kann eine Variable übergeben werden, die die Einschränkung erfüllt. Im Gegensatz dazu ist eine nicht generische Methode mit einem Parameter vom Typ Schnittstelle darauf beschränkt, Variablen dieses genauen Schnittstellentyps zu akzeptieren.Eine Methode mit dem generischen Typparameter T kann generische Sammlungen von T akzeptieren. Eine Methode, die einen akzeptiert, kann einen
IList<T> where T:IAnimal
akzeptierenList<SiameseCat>
, aber eine Methode, die einen wollte, kannIList<Animal>
dies nicht.Eine Einschränkung kann manchmal eine Schnittstelle in Bezug auf den generischen Typ spezifizieren, z
where T:IComparable<T>
.Eine Struktur, die eine Schnittstelle implementiert, kann als Werttyp beibehalten werden, wenn sie an eine Methode übergeben wird, die einen eingeschränkten generischen Parameter akzeptiert, muss jedoch bei der Übergabe als Schnittstellentyp umrahmt werden. Dies kann eine enorme Auswirkung auf die Geschwindigkeit haben.
Ein generischer Parameter kann mehrere Einschränkungen haben, während es keine andere Möglichkeit gibt, einen Parameter des Typs "Einige Typen, die sowohl IFoo als auch IBar implementieren" anzugeben. Manchmal kann dies ein zweischneidiges Schwert sein, da es für Code, der einen Parameter vom Typ erhalten
IFoo
hat, sehr schwierig ist, ihn an eine solche Methode zu übergeben, die eine doppelte generische Einschränkung erwartet, selbst wenn die betreffende Instanz alle Einschränkungen erfüllen würde.Wenn die Verwendung eines generischen Parameters in einer bestimmten Situation keinen Vorteil bietet, akzeptieren Sie einfach einen Parameter des Schnittstellentyps. Die Verwendung eines Generikums zwingt das Typsystem und JITter dazu, zusätzliche Arbeit zu leisten. Wenn es also keinen Nutzen gibt, sollte man dies nicht tun. Andererseits ist es sehr verbreitet, dass mindestens einer der oben genannten Vorteile zutrifft.
quelle