Welche Methode empfiehlt sich zum Eingeben und Prüfen?
%Vor%ODER
%Vor%Mindestens gibt es zwei Möglichkeiten zum Gießen und eine für die Typprüfung. Jeder hat seinen eigenen Zweck und hängt von der Situation ab:
Normalerweise tun Sie das, wenn Sie absolut sicher sind, ob das gegebene Objekt von diesem Typ ist. Eine Situation, in der Sie es verwenden, wenn Sie einen Event-Handler abonniert haben und Sie das Absenderobjekt auf den richtigen Typ umwandeln, um damit zu arbeiten.
%Vor%Dies wird normalerweise verwendet, wenn Sie nicht wissen, ob Sie wirklich diese Art von Typ haben. Also versuche es einfach und wenn es nicht möglich ist gib einfach eine Null zurück. Ein häufiges Beispiel wäre, wenn Sie nur etwas tun müssen, wenn eine Schnittstelle erfüllt ist:
%Vor%Dies wird selten richtig verwendet. Diese Typprüfung ist nur dann sinnvoll, wenn Sie nur wissen müssen, ob etwas von einem bestimmten Typ ist, aber Sie müssen dieses Objekt nicht verwenden.
%Vor%Ich denke, das ist eine gute Frage, die eine ernsthafte und detaillierte Antwort verdient. Typumwandlungen sind C # sind eine Menge verschiedener Dinge tatsächlich.
Im Gegensatz zu C # sind Sprachen wie C ++ sehr streng, daher verwende ich die Benennung als Referenz. Ich denke immer, es ist am besten zu verstehen, wie die Dinge funktionieren, also werde ich hier alles für Sie mit den Details abbrechen. Hier geht es:
Dynamische Umsetzungen und statische Umsetzungen
C # hat Werttypen und Referenztypen. Referenztypen folgen immer einer Vererbungskette, beginnend mit Objekt.
Wenn du (Foo)myObject
machst, machst du eigentlich eine dynamische Besetzung , und wenn du (object)myFoo
(oder einfach object o = myFoo
) machst, machst du eine > statische Besetzung .
Eine dynamische Umwandlung erfordert eine Typüberprüfung, dh die Laufzeitumgebung prüft, ob das Objekt, an das Sie eine Umwandlung vornehmen, vom Typ ist. Schließlich werfen Sie den Vererbungsbaum nieder, sodass Sie genauso gut auf etwas anderes zurückgreifen können. Wenn dies der Fall ist, erhalten Sie eine InvalidCastException
. Aus diesem Grund benötigen dynamische Umwandlungen Informationen zum Laufzeittyp (z. B. muss die Laufzeitumgebung wissen, welches Objekt über welchen Typ verfügt).
Eine statische Umwandlung erfordert keine Typüberprüfung. In diesem Fall werden wir in der Vererbungsstruktur angezeigt, sodass wir bereits wissen , dass die Typumwandlung erfolgreich ist. Es wird niemals eine Ausnahme ausgelöst.
Werttyp-Umwandlungen sind eine spezielle Art von Umwandlung, die verschiedene Werttypen konvertiert (zB von float nach int). Ich werde später darauf eingehen.
As, is, cast
In IL werden nur castclass
(Cast) und isinst
(as) unterstützt. Der Operator is
wird als as
mit einem Null-Check implementiert und ist nicht mehr als eine bequeme Kurzschreibweise für die Kombination von beiden. In C # könnten Sie is
wie folgt schreiben: (myObject as MyFoo) != null
.
as
überprüft einfach, ob ein Objekt einen bestimmten Typ hat, und gibt null zurück, wenn dies nicht der Fall ist. Für den Fall static cast können wir diese Kompilierzeit bestimmen, für den Fall dynamic cast müssen wir dies zur Laufzeit überprüfen.
(...)
wirft erneut nach, ob der Typ korrekt ist, und wirf eine Ausnahme aus, wenn dies nicht der Fall ist. Es ist im Grunde das gleiche wie as
, aber mit einem throw statt einem null
result. Das könnte Sie wundern, warum as
nicht als Exception-Handler implementiert ist - nun, wahrscheinlich, weil Ausnahmen relativ langsam sind.
Boxen
Ein spezieller Cast-Typ tritt auf, wenn Sie box
einem Werttyp in ein Objekt eingeben. Was grundsätzlich passiert, ist, dass die .NET-Laufzeit den Werttyp auf dem Heap kopiert (mit einigen Typinformationen) und die Adresse als Referenztyp zurückgibt. Mit anderen Worten: Es konvertiert einen Werttyp in einen Referenztyp.
Das passiert, wenn Sie Code wie folgt haben:
%Vor%Beim Unboxing müssen Sie einen Typ angeben. Während der Unboxing-Operation wird der Typ überprüft (wie der Fall dynamic cast , aber er ist viel einfacher, da die Vererbungskette eines Werttyps trivial ist). Wenn der Typ übereinstimmt, wird der Wert erneut kopiert der Stapel.
Sie könnten erwarten, dass Werttyp-Umwandlungen implizit für das Boxen sind - nun, aus dem oben Gesagten sind sie nicht. Die einzige Unboxing-Operation, die zulässig ist, ist das Unboxing auf den genauen Werttyp. Mit anderen Worten:
%Vor%Werttyp-Umwandlungen
Wenn Sie float
auf int
umwandeln, konvertieren Sie den Wert im Grunde . Für die Basistypen (IntPtr, (u) int 8/16/32/64, float, double) sind diese Konvertierungen in IL als conv_*
-Instruktionen vordefiniert, die äquivalent zu Bit-Umwandlungen sind (int8 - & gt; int16 ), trunction (int16 - & gt; int8) und Konvertierung (float - & gt; int32).
Hier geschehen einige lustige Dinge. Die Laufzeit scheint auf einer Vielzahl von 32-Bit-Werten auf dem Stack zu funktionieren, sodass Sie Konvertierungen selbst an Orten benötigen, an denen Sie sie nicht erwarten würden. Betrachten Sie zum Beispiel:
%Vor%Die Erweiterung des Schildes könnte schwierig sein, den Kopf herumzulegen. Computer speichern vorzeichenbehaftete ganzzahlige Werte als 1-Komplemente. In Hex-Notation, int8, bedeutet dies, dass der Wert -1 0xFF ist. Was passiert also, wenn wir es auf ein int32-Objekt umwandeln? Auch hier ist der 1-Komplement-Wert von -1 0xFFFFFFFF - also müssen wir das höchstwertige Bit zum Rest der "addierten" Bits propagieren. Wenn wir unsigned Erweiterungen machen, müssen wir Nullen propagieren.
Um diesen Punkt zu verdeutlichen, hier ein einfacher Testfall:
%Vor%Die erste Umwandlung in int ist hier null, die zweite Umwandlung in int ist vorzeichenerweitert. Sie können auch mit der Formatzeichenfolge "x8" spielen, um die hexadezimale Ausgabe zu erhalten.
Für den genauen Unterschied zwischen Bit-Casts, Trunkierung und Konvertierung beziehe ich mich auf die LLVM Dokumentation Das erklärt die Unterschiede. Suchen Sie nach sext
/ zext
/ bitcast
/ fptosi
und alle Varianten.
Implizite Typkonvertierung
Eine andere Kategorie bleibt übrig, und das sind die Konvertierungsoperatoren. In MSDN wird beschrieben, wie Sie die Konvertierungsoperatoren überlasten können. Grundsätzlich können Sie Ihre eigene Konvertierung implementieren, indem Sie einen Operator überladen. Wenn Sie möchten, dass der Benutzer explizit angibt, dass Sie beabsichtigen zu casten, fügen Sie das Schlüsselwort explicit
hinzu. Wenn Sie möchten, dass implizite Konvertierungen automatisch ausgeführt werden, fügen Sie implicit
hinzu. Im Grunde erhalten Sie:
... nach dem du Sachen wie
machen kannst %Vor%Bewährte Verfahren
Machen Sie sich zuerst mit den Unterschieden vertraut, was bedeutet, dass Sie kleine Testprogramme implementieren, bis Sie den Unterschied zwischen all dem verstehen. Es gibt keinen Ersatz, um zu verstehen, wie Dinge funktionieren.
Dann würde ich mich an diese Praktiken halten:
Wenn bei der zweiten Methode die Umwandlung fehlschlägt, wird eine Ausnahme ausgelöst.
Beim Casting mit as
können Sie nur Referenztypen verwenden. Wenn Sie also einen Werttyp eingeben, müssen Sie weiterhin int e = (int) o;
method verwenden.
Eine gute Faustregel lautet: Wenn Sie dem Objekt null als Wert zuweisen können, können Sie die Umwandlung mit as
eingeben.
Das heißt, der Null-Vergleich ist schneller als das Werfen und Abfangen einer Ausnahme. Daher sollte as
in den meisten Fällen schneller sein.
Ich kann ehrlich nicht mit Sicherheit sagen, ob dies mit Ihrem is
-Überblick gilt. Es kann unter bestimmten Multi-Threading-Bedingungen fehlschlagen, wenn ein anderer Thread das Objekt ändert, das Sie übertragen.
Ich würde den Operator as
(safe-cast) verwenden, wenn ich das Objekt nach dem Casting verwenden möchte. Dann überprüfe ich auf Null und arbeite mit der Instanz. Diese Methode ist effizienter als is
+ explizite Umwandlung
Im Allgemeinen ist der as-Operator effizienter, weil er den Cast-Wert zurückgibt, wenn die Umwandlung erfolgreich durchgeführt werden kann. Der Operator is gibt nur einen booleschen Wert zurück. Es kann daher verwendet werden, wenn Sie nur den Typ eines Objekts bestimmen möchten, es aber nicht tatsächlich umsetzen müssen.
(Weitere Informationen hier ).
Ich bin nicht sicher , aber ich denke, dass is
as
unter der Haube verwendet und nur zurückgibt, wenn das Objekt nach dem Casting null ist (im Falle von Referenztypen) ) / Es wurde eine Ausnahme ausgelöst (im Fall von Werttypen ) oder nicht.
Nun, es ist eine Frage des Geschmacks und der Besonderheiten des Problems, mit denen Sie es zu tun haben. Lassen Sie uns zwei Beispiele mit generischen Methoden betrachten.
Für generische Methode mit 'class' constraint (der sicherste Ansatz mit Double Cast):
%Vor%Sie können auch so etwas tun (eine Cast-Operation hier, aber eine definierte Variable vom Typ, die korrekt sein kann oder nicht):
%Vor%Für generische Methode mit Einschränkung 'struct':
%Vor%Einfaches Szenario mit expliziter Umsetzung:
%Vor%Wir können die explizite Umwandlung hier verwenden, weil wir uns zu 100% sicher sind, welche Typen wir verwenden.