=============
UPDATE: Ich habe diese Antwort als Grundlage für diesen Blogeintrag verwendet:
Warum erlauben ref- und out-Parameter keine Typvariation?
Weitere Kommentare zu diesem Thema finden Sie auf der Blog-Seite. Danke für die tolle Frage.
=============
Nehmen wir an , Sie haben Klassen Animal
, Mammal
, Reptile
, Giraffe
, Turtle
und Tiger
, mit den offensichtlichen Subklassifizieren Beziehungen.
Angenommen, Sie haben eine Methode void M(ref Mammal m)
. M
kann sowohl lesen als auch schreiben m
.
Können Sie eine Variable vom Typ passieren Animal
zu M
?
Nein. Diese Variable könnte a enthalten Turtle
, geht jedoch M
davon aus, dass sie nur Säugetiere enthält. A Turtle
ist nicht a Mammal
.
Schlussfolgerung 1 : ref
Parameter können nicht "größer" gemacht werden. (Es gibt mehr Tiere als Säugetiere, daher wird die Variable "größer", weil sie mehr Dinge enthalten kann.)
Können Sie eine Variable vom Typ passieren Giraffe
zu M
?
Nein M
kann schreiben m
, und M
vielleicht einen schreiben wollen Tiger
in m
. Jetzt haben Sie ein Tiger
in eine Variable eingefügt, die tatsächlich vom Typ ist Giraffe
.
Schlussfolgerung 2 : ref
Parameter können nicht "kleiner" gemacht werden.
Nun überlegen Sie N(out Mammal n)
.
Können Sie eine Variable vom Typ passieren Giraffe
zu N
?
Nein, N
kann schreiben n
und N
möchte vielleicht eine schreiben Tiger
.
Schlussfolgerung 3 : out
Parameter können nicht "kleiner" gemacht werden.
Können Sie eine Variable vom Typ passieren Animal
zu N
?
Hmm.
Gut, warum nicht? N
kann nicht lesen n
, es kann nur darauf schreiben, oder? Du schreibst eine Tiger
in eine Variable vom Typ Animal
und bist fertig, oder?
Falsch. Die Regel lautet nicht " N
kann nur schreiben n
".
Die Regeln sind kurz:
1) N
muss schreiben, n
bevor N
normal zurückgegeben wird. (Bei N
Würfen sind alle Wetten ungültig.)
2) N
muss etwas schreiben, n
bevor es etwas liest n
.
Das erlaubt diese Abfolge von Ereignissen:
- Deklarieren Sie ein Feld
x
vom Typ Animal
.
- Übergeben Sie
x
als out
Parameter N
.
N
schreibt ein Tiger
in n
, was ein Alias für ist x
.
- In einem anderen Thread schreibt jemand ein
Turtle
in x
.
N
versucht, den Inhalt von zu lesen n
, und entdeckt Turtle
eine Variable, die seiner Meinung nach vom Typ ist Mammal
.
Natürlich wollen wir das illegal machen.
Schlussfolgerung 4 : out
Parameter können nicht "größer" gemacht werden.
Endgültige Schlussfolgerung : Weder ref
noch out
Parameter dürfen ihre Typen variieren. Andernfalls wird die überprüfbare Typensicherheit verletzt.
Wenn Sie diese Probleme in der grundlegenden Typentheorie interessieren, lesen Sie meine Reihe darüber, wie Kovarianz und Kontravarianz in C # 4.0 funktionieren .
out
Parameter nicht "größer" gemacht werden? Die von Ihnen beschriebene Sequenz kann auf jede Variable angewendet werden, nicht nur aufout
Parametervariablen. Und auch der Leser muss den Argumentwert auf setzen,Mammal
bevor er versucht, darauf zuzugreifen, da erMammal
natürlich fehlschlagen kann, wenn er nichtDenn in beiden Fällen müssen Sie dem ref / out-Parameter einen Wert zuweisen können.
Wenn Sie versuchen, b als Referenz an die Foo2-Methode zu übergeben, und in Foo2 versuchen, a = new A () zuzuweisen, ist dies ungültig.
Aus demselben Grund können Sie nicht schreiben:
quelle
Sie haben mit dem klassischen OOP-Problem der Kovarianz (und Kontravarianz) zu kämpfen , siehe Wikipedia : So sehr diese Tatsache den intuitiven Erwartungen widerspricht, ist es mathematisch unmöglich, die Ersetzung abgeleiteter Klassen anstelle von Basisklassen durch veränderbare (zuweisbare) Argumente (und) zuzulassen auch Container, deren Gegenstände aus dem gleichen Grund zuweisbar sind), wobei das Liskov-Prinzip weiterhin eingehalten wird . Warum das so ist, wird in den vorhandenen Antworten skizziert und in diesen Wiki-Artikeln und deren Links eingehender untersucht.
OOP-Sprachen, die dies zu tun scheinen, während sie traditionell statisch typsicher bleiben, sind "betrügerisch" (Einfügen versteckter dynamischer Typprüfungen oder Überprüfung aller Quellen zur Überprüfung während der Kompilierung); Die grundlegende Wahl lautet: Geben Sie entweder diese Kovarianz auf und akzeptieren Sie die Verwirrung der Praktizierenden (wie hier C #), oder gehen Sie zu einem dynamischen Typisierungsansatz über (wie es die allererste OOP-Sprache, Smalltalk, getan hat) oder gehen Sie zu unveränderlich über (Single-) Zuweisungsdaten wie funktionale Sprachen (bei Unveränderlichkeit können Sie die Kovarianz unterstützen und auch andere verwandte Rätsel vermeiden, z. B. die Tatsache, dass Sie in einer Welt mit veränderlichen Daten kein Rechteck der quadratischen Unterklasse haben können).
quelle
Erwägen:
Dies würde die Typensicherheit verletzen
quelle
var
war total falsch. Fest.Während die anderen Antworten die Gründe für dieses Verhalten kurz und bündig erklärt haben, ist es meiner Meinung nach erwähnenswert, dass Sie, wenn Sie wirklich etwas in dieser Art tun müssen, ähnliche Funktionen erreichen können, indem Sie Foo2 zu einer generischen Methode machen:
quelle
Weil das Geben
Foo2
einesref B
zu einem fehlerhaften Objekt führen würde, weilFoo2
nur einA
Teil davon gefüllt werden kannB
.quelle
Sagt Ihnen der Compiler nicht, dass Sie das Objekt explizit umwandeln sollen, damit Sie sicher sind, dass Sie Ihre Absichten kennen?
quelle
Aus Sicherheitsgründen sinnvoll, aber ich hätte es vorgezogen, wenn der Compiler eine Warnung anstelle eines Fehlers ausgegeben hätte, da es legitime Verwendungen von polymoprhischen Objekten gibt, die als Referenz übergeben werden. z.B
Dies wird nicht kompiliert, aber würde es funktionieren?
quelle
Wenn Sie praktische Beispiele für Ihre Typen verwenden, werden Sie Folgendes sehen:
Und jetzt haben Sie Ihre Funktion, die den Vorfahren übernimmt ( dh
Object
):Was kann daran möglicherweise falsch sein?
Sie haben es gerade geschafft, eine zuzuweisen
Bitmap
, IhremSqlConnection
.Das ist nicht gut.
Versuchen Sie es erneut mit anderen:
Du hast ein
OracleConnection
Over-Top von dir gestopftSqlConnection
.quelle
In meinem Fall hat meine Funktion ein Objekt akzeptiert und ich konnte nichts einsenden, also habe ich es einfach getan
Und das funktioniert
Mein Foo ist in VB.NET und es prüft auf Typ im Inneren und macht viel Logik
Ich entschuldige mich, wenn meine Antwort doppelt ist, andere aber zu lang waren
quelle