In C # kann das out
Schlüsselwort auf zwei verschiedene Arten verwendet werden.
Als Parametermodifikator, in dem ein Argument als Referenz übergeben wird
class OutExample { static void Method(out int i) { i = 44; } static void Main() { int value; Method(out value); // value is now 44 } }
Als Typparameter-Modifikator zur Angabe der Kovarianz .
// Covariant interface. interface ICovariant<out R> { } // Extending covariant interface. interface IExtCovariant<out R> : ICovariant<R> { } // Implementing covariant interface. class Sample<R> : ICovariant<R> { } class Program { static void Test() { ICovariant<Object> iobj = new Sample<Object>(); ICovariant<String> istr = new Sample<String>(); // You can assign istr to iobj because // the ICovariant interface is covariant. iobj = istr; } }
Meine Frage ist: warum?
Für einen Anfänger scheint die Verbindung zwischen beiden nicht intuitiv zu sein . Die Verwendung mit Generika scheint nichts mit der Übergabe von Referenzen zu tun zu haben.
Ich habe zuerst gelernt, was out
mit der Weitergabe von Argumenten durch Bezugnahme zu tun hat, und dies hat mein Verständnis der Verwendung der Definition von Kovarianz mit Generika behindert.
Gibt es einen Zusammenhang zwischen diesen Verwendungen, den ich vermisse?
c#
terminology
language-design
keywords
Rowan Freeman
quelle
quelle
System.Func<in T, out TResult>
Delegaten ansehen .Antworten:
Es besteht eine Verbindung, die jedoch etwas locker ist. In C # stehen die Schlüsselwörter ´in´ und ´out´, wie der Name schon sagt, für Eingabe und Ausgabe. Dies ist bei Ausgabeparametern sehr klar, aber weniger sauber, was es mit Vorlagenparametern zu tun hat.
Schauen wir uns das Liskov-Substitutionsprinzip an :
Sehen Sie, wie Kontravarianz mit Eingabe und Kovarianz mit Ausgabe verbunden ist? Wenn Sie in C # eine Vorlagenvariable mit
out
kennzeichnen, um sie kovariant zu machen, beachten Sie jedoch, dass Sie dies nur tun können, wenn der angegebene Typparameter nur als Ausgabe angezeigt wird (Funktionsrückgabetyp). Folgendes ist also ungültig:Ähnlich, wenn Sie einen Typparameter mit kennzeichnen
in
, bedeutet dies, dass Sie ihn nur als Eingabe (Funktionsparameter) verwenden können. Folgendes ist also ungültig:Zusammenfassend bedeutet die Verbindung mit dem
out
Schlüsselwort, dass es sich bei Funktionsparametern um einen Ausgabeparameter handelt , und bei Typparametern bedeutet dies, dass der Typ nur im Ausgabekontext verwendet wird.System.Func
ist auch ein gutes Beispiel, was rwong in seinem Kommentar erwähnt hat. InSystem.Func
allen Eingabeparametern wird mit flagiertin
, und der Ausgabeparameter wird mit flagiertout
. Der Grund ist genau das, was ich beschrieben habe.quelle
@ Gábor hat den Zusammenhang bereits erklärt (Kontravarianz für alles, was "rein" geht, Kovarianz für alles, was "raus" geht), aber warum Schlüsselwörter überhaupt wiederverwenden?
Schlüsselwörter sind sehr teuer. Sie können sie nicht als Bezeichner in Ihren Programmen verwenden. Aber es gibt nur so viele Wörter in der englischen Sprache. Manchmal treten Konflikte auf, und Sie müssen Ihre Variablen, Methoden, Felder, Eigenschaften, Klassen, Schnittstellen oder Strukturen umständlich umbenennen, um Konflikte mit einem Schlüsselwort zu vermeiden. Wenn Sie beispielsweise eine Schule modellieren, wie nennen Sie eine Klasse? Sie können es nicht als Klasse bezeichnen, da
class
es sich um ein Schlüsselwort handelt!Hinzufügen ein Schlüsselwort in eine Sprache ist noch mehr teuer. Grundsätzlich macht jeder Code, der dieses Schlüsselwort als Bezeichner verwendet, illegal, wodurch die Abwärtskompatibilität überall beeinträchtigt wird.
Die Schlüsselwörter
in
und warenout
bereits vorhanden, sodass sie einfach wiederverwendet werden konnten.Sie konnten haben kontextuelle Schlüsselwörter hinzugefügt, die nur Schlüsselwörter im Rahmen einer Typparameterliste, aber welche Keywords würden sie gewählt haben?
covariant
undcontravariant
?+
und-
(wie Scala zum Beispiel)?super
undextends
wie Java? Können Sie sich auf Anhieb daran erinnern, welche Parameter kovariant und kontravariant sind?Mit der aktuellen Lösung gibt es eine nette Mnemonik: Ausgabetypparameter erhalten das
out
Schlüsselwort, Eingabetypparameter erhalten dasin
Schlüsselwort. Beachten Sie die schöne Symmetrie mit den Methodenparametern: Ausgabeparameter erhalten dasout
Schlüsselwort, Eingabeparameter erhalten dasin
Schlüsselwort (naja, eigentlich überhaupt kein Schlüsselwort, da die Eingabe die Standardeinstellung ist, aber Sie haben die Idee).[Hinweis: Wenn Sie sich den Bearbeitungsverlauf ansehen, werden Sie feststellen, dass ich die beiden ursprünglich in meinem Einleitungssatz vertauscht habe. Und ich habe in dieser Zeit sogar eine positive Bewertung bekommen! Dies zeigt nur, wie wichtig diese Mnemonik wirklich ist.]
quelle
interface Accepter<in T> { void Accept(T it);};
, eineAccepter<Foo<T>>
akzeptiertT
als Eingangsparameter , wennFoo<T>
es als Ausgabeparameter übernimmt, und umgekehrt. So contra -variance. Im Gegensatz dazu hatinterface ISupplier<out T> { T get();};
einSupplier<Foo<T>>
Wille jede Art von VarianzFoo
- also Co- Varianz.