Ich weiß, dass Java parametrischen Polymorphismus (Generics) mit Löschung implementiert. Ich verstehe, was Löschen ist.
Ich weiß, dass C # parametrischen Polymorphismus mit Reifizierung implementiert. Ich weiß, das kann dich zum Schreiben bringen
public void dosomething(List<String> input) {}
public void dosomething(List<Int> input) {}
oder dass Sie zur Laufzeit wissen können, was der Typparameter eines parametrisierten Typs ist, aber ich verstehe nicht, was es ist .
- Was ist ein reifizierter Typ?
- Was ist ein verdichteter Wert?
- Was passiert, wenn ein Typ / Wert geändert wird?
c#
generics
reification
Martijn
quelle
quelle
if
Replikation ist der Prozess der Konvertierung einesswitch
Konstrukts zurück in einif
/else
, als es zuvor von einemif
/else
in ein konvertiert worden warswitch
...Antworten:
Reifikation ist der Prozess, eine abstrakte Sache zu nehmen und eine konkrete Sache zu schaffen.
Der Begriff Reifizierung in C # -Generika bezieht sich auf den Prozess, durch den eine generische Typdefinition und ein oder mehrere generische Typargumente (das abstrakte Ding) kombiniert werden, um einen neuen generischen Typ (das konkrete Ding) zu erstellen .
Begriff anders es, ist es der Prozess der Übernahme der Definition
List<T>
undint
und eine Herstellung von Beton -List<int>
Typ.Vergleichen Sie die folgenden Ansätze, um es besser zu verstehen:
In Java-Generika wird eine generische Typdefinition in im Wesentlichen einen konkreten generischen Typ umgewandelt, der von allen zulässigen Typargumentkombinationen gemeinsam genutzt wird. Daher werden mehrere Typen (Quellcodeebene) einem Typ (Binärebene) zugeordnet . Infolgedessen werden Informationen zu den Typargumenten einer Instanz in dieser Instanz verworfen (Typlöschung) .
In C # -Generics wird die generische Typdefinition zur Laufzeit im Speicher beibehalten. Immer wenn ein neuer konkreter Typ erforderlich ist, kombiniert die Laufzeitumgebung die generische Typdefinition und die Typargumente und erstellt den neuen Typ (Reifizierung). Daher erhalten wir zur Laufzeit für jede Kombination der Typargumente einen neuen Typ .
System.Type
Klasse jederzeit selbst eine Überprüfung durchführen (selbst wenn die von Ihnen instanziierte bestimmte generische Typargumentkombination dies nicht getan hat). t direkt in Ihrem Quellcode erscheinen).In C ++ - Vorlagen wird die Vorlagendefinition zur Kompilierungszeit im Speicher beibehalten. Immer wenn im Quellcode eine neue Instanziierung eines Vorlagentyps erforderlich ist, kombiniert der Compiler die Vorlagendefinition und die Vorlagenargumente und erstellt den neuen Typ. So erhalten wir zur Kompilierungszeit für jede Kombination der Vorlagenargumente einen eindeutigen Typ .
quelle
Reifizierung bedeutet im Allgemeinen (außerhalb der Informatik) "etwas Reales machen".
Bei der Programmierung wird etwas geändert, wenn wir in der Sprache selbst auf Informationen darüber zugreifen können.
Nehmen wir für zwei vollständig nicht generikabezogene Beispiele für etwas, das C # tut und nicht geändert hat, Methoden und Speicherzugriff.
OO-Sprachen haben im Allgemeinen Methoden (und viele, die keine ähnlichen Funktionen haben , jedoch nicht an eine Klasse gebunden sind). Als solches können Sie eine Methode in einer solchen Sprache definieren, aufrufen, möglicherweise überschreiben und so weiter. Nicht in all diesen Sprachen können Sie die Methode selbst als Daten für ein Programm behandeln. Mit C # (und wirklich mit .NET anstelle von C #) können Sie
MethodInfo
Objekte verwenden, die die Methoden darstellen, sodass in C # Methoden angepasst werden. Methoden in C # sind "erstklassige Objekte".Alle praktischen Sprachen haben einige Mittel, um auf den Speicher eines Computers zuzugreifen. In einer einfachen Sprache wie C können wir uns direkt mit der Zuordnung zwischen numerischen Adressen befassen, die vom Computer verwendet werden. Dies
int* ptr = (int*) 0xA000000; *ptr = 42;
ist also sinnvoll (solange wir einen guten Grund haben zu vermuten, dass der Zugriff auf die Speicheradresse0xA000000
auf diese Weise gewonnen hat. t etwas in die Luft jagen). In C # ist dies nicht sinnvoll (wir können es in .NET fast erzwingen, aber mit der .NET-Speicherverwaltung ist es nicht sehr wahrscheinlich, dass Dinge verschoben werden). C # hat keine verifizierten Speicheradressen.So, wie refied Mittel „made real“ ein „verdinglicht Typ“ ist eine Art können wir „Rede über“ in der Sprache , in Frage.
Bei Generika bedeutet dies zwei Dinge.
Eines ist, dass
List<string>
es sich um einen Typ handelt, der genauso iststring
oderint
ist. Wir können diesen Typ vergleichen, seinen Namen erhalten und uns danach erkundigen:Dies hat zur Folge, dass wir über die Parametertypen einer generischen Methode (oder einer Methode einer generischen Klasse) innerhalb der Methode selbst "sprechen" können:
Zu viel zu tun ist in der Regel "stinkend", hat aber viele nützliche Fälle. Schauen Sie sich zum Beispiel an:
Dies führt nicht viele Vergleiche zwischen dem Typ
TSource
und verschiedenen Typen für unterschiedliche Verhaltensweisen durch (im Allgemeinen ein Zeichen dafür, dass Sie überhaupt keine Generika hätten verwenden sollen), aber es wird zwischen einem Codepfad für Typen aufgeteilt, die sein könnennull
(solltenull
if zurückgeben Kein Element gefunden und darf keine Vergleiche anstellen, um das Minimum zu finden, wenn eines der verglichenen Elemente istnull
) und den Codepfad für Typen, die nicht gefunden werden könnennull
(sollte geworfen werden, wenn kein Element gefunden wurde, und muss sich nicht um die Möglichkeit vonnull
Elementen kümmern ).Da dies
TSource
innerhalb der Methode "real" ist, kann dieser Vergleich entweder zur Laufzeit oder zur Jitting-Zeit durchgeführt werden (im Allgemeinen Jitting-Zeit, sicherlich würde der obige Fall dies zur Jitting-Zeit tun und keinen Maschinencode für den nicht genommenen Pfad erzeugen), und wir haben a separate "echte" Version der Methode für jeden Fall. (Als Optimierung wird der Maschinencode für verschiedene Methoden für verschiedene Parameter vom Typ Referenztyp gemeinsam genutzt, da dies nicht beeinflusst werden kann und wir daher die Menge des ausgestoßenen Maschinencodes reduzieren können.)(Es ist nicht üblich , über Verdinglichung von generischen Typen in C # zu sprechen , wenn Sie auch mit Java beschäftigen, denn in C # wir diese Verdinglichung nehmen nur für selbstverständlich, alle Arten verdinglicht werden in Java, nicht-generischen Typen werden als. Verdinglicht , weil das ist eine Unterscheidung zwischen ihnen und generischen Typen).
quelle
Min
sinnvoll, das zu tun, was oben sinnvoll ist? Es ist sehr schwer, sein dokumentiertes Verhalten anders zu erfüllen.Enumerable.Min<TSource>
darin, dass es nicht für Nichtreferenztypen in eine leere Sammlung wirft, sondern Standard zurückgibt (TSource) und wird nur als "Gibt den Mindestwert in einer generischen Sequenz zurück" dokumentiert. Ich würde argumentieren, dass beide auf eine leere Sammlung werfen sollten oder dass ein "Null" -Element als Basislinie übergeben werden sollte, und der Komparator / Vergleichsfunktion sollte immer übergeben werden)Wie Duffymo bereits bemerkte , ist "Verdinglichung" nicht der Hauptunterschied.
In Java dienen Generika im Wesentlichen dazu, die Unterstützung zur Kompilierungszeit zu verbessern. Sie können stark typisierte, z. B. Sammlungen in Ihrem Code verwenden und die Typensicherheit für Sie erledigen. Dies existiert jedoch nur zur Kompilierungszeit - der kompilierte Bytecode hat keine Vorstellung von Generika mehr; Alle generischen Typen werden in "konkrete" Typen umgewandelt (
object
wenn der generische Typ unbegrenzt ist), wobei nach Bedarf Typkonvertierungen und Typprüfungen hinzugefügt werden.In .NET sind Generika ein wesentliches Merkmal der CLR. Wenn Sie einen generischen Typ kompilieren, bleibt dieser in der generierten IL generisch. Es wird nicht nur wie in Java in nicht generischen Code umgewandelt.
Dies hat verschiedene Auswirkungen auf die Funktionsweise von Generika in der Praxis. Beispielsweise:
SomeType<?>
es Ihnen ermöglichen, jede konkrete Implementierung eines bestimmten generischen Typs zu übergeben. C # kann dies nicht - jeder spezifische ( reifizierte ) generische Typ ist ein eigener Typ.object
. Dies kann sich auf die Leistung auswirken, wenn Werttypen in solchen Generika verwendet werden. Wenn Sie in C # einen Werttyp in einem generischen Typ verwenden, bleibt dieser ein Werttyp.Nehmen wir an, Sie haben einen
List
generischen Typ mit einem generischen Argument, um ein Beispiel zu geben . In JavaList<String>
undList<Int>
wird zur Laufzeit genau derselbe Typ sein - die generischen Typen existieren nur für Code zur Kompilierungszeit. Alle Anrufe zBGetValue
wird transformiert werden(String)GetValue
und(Int)GetValue
jeweils.In C #
List<string>
undList<int>
sind zwei verschiedene Typen. Sie sind nicht austauschbar und ihre Typensicherheit wird auch zur Laufzeit erzwungen. Egal was Sie tun, esnew List<int>().Add("SomeString")
wird niemals funktionieren - der zugrunde liegende Speicher inList<int>
ist wirklich ein ganzzahliges Array, während es in Java notwendigerweise einobject
Array ist. In C # gibt es keine Casts, kein Boxen usw.Dies sollte auch deutlich machen, warum C # nicht dasselbe tun kann wie Java
SomeType<?>
. In Java sind alle generischen Typen, von denen "abgeleitet"SomeType<?>
wird, genau der gleiche Typ. In C # sind alle verschiedenen spezifischenSomeType<T>
s ihre eigenen separaten Typen. Wenn Sie die Überprüfungen zur Kompilierungszeit entfernen, ist es möglich,SomeType<Int>
stattdessen zu bestehenSomeType<String>
(und dasSomeType<?>
bedeutet nur, dass Sie die Überprüfungen zur Kompilierungszeit für den angegebenen generischen Typ ignorieren). In C # ist dies nicht möglich, auch nicht für abgeleitete Typen (das heißt, Sie können dies nicht tunList<object> list = (List<object>)new List<string>();
, obwohlstring
es von abgeleitet istobject
).Beide Implementierungen haben ihre Vor- und Nachteile. Es gab einige Male, in denen ich es geliebt hätte, nur
SomeType<?>
als Argument in C # zuzulassen - aber es macht einfach keinen Sinn, wie C # -Generika funktionieren.quelle
List<>
,Dictionary<,>
und so weiter in C #, aber der Abstand zwischen diesem und einem bestimmten konkreten Liste oder Wörterbuch nimmt ziemlich viel Reflexion Brücke. Abweichungen bei den Schnittstellen helfen in einigen Fällen, in denen wir diese Lücke vielleicht einmal leicht überbrücken wollten, aber nicht bei allen.List<>
damit einen neuen bestimmten generischen Typ instanziieren - es bedeutet jedoch immer noch, den gewünschten bestimmten Typ zu erstellen. Aber Sie könnenList<>
zum Beispiel nicht als Argument verwenden. Aber ja, zumindest können Sie so die Lücke durch Reflexion schließen.T
kann eine lagerortspezifische Typ Bedingung erfüllenU
sind , wennT
undU
vom gleichen Typ sind , oderU
ist ein Typ, der einen Verweis auf eine Instanz halten kannT
. Es wäre nicht möglich, einen Speicherort vom Typ sinnvoll zu haben,SomeType<?>
aber theoretisch wäre es möglich, eine generische Einschränkung dieses Typs zu haben.Reification ist ein objektorientiertes Modellierungskonzept.
Reify ist ein Verb, das bedeutet "etwas Abstraktes real werden lassen" .
Wenn Sie objektorientiert programmieren, ist es üblich, reale Objekte als Softwarekomponenten zu modellieren (z. B. Fenster, Schaltfläche, Person, Bank, Fahrzeug usw.).
Es ist auch üblich, abstrakte Konzepte in Komponenten umzuwandeln (z. B. WindowListener, Broker usw.).
quelle