Ich habe eine Funktion, die a double
auf string
Werte wirft .
string variable = "5.00";
double varDouble = (double)variable;
Eine Codeänderung wurde eingecheckt und das Projekt wird mit dem Fehler erstellt: System.InvalidCastException: Specified cast is not valid.
Nachdem Sie jedoch Folgendes getan haben ...
string variable = "5.00";
double varDouble = Convert.ToDouble(variable);
... das Projekt fehlerfrei erstellt wird.
Was ist der Unterschied zwischen Gießen und Verwenden der Convert.To()
Methode? Warum wirft Casting ein Exception
und benutzt das Convert.To()
nicht?
Antworten:
Auch wenn Sie können sie irgendwie als gleichwertig sehen sind sie in Zweck völlig anders. Versuchen wir zunächst zu definieren, was eine Besetzung ist:
Es ist ein bisschen generisch und entspricht irgendwie einer Konvertierung, da Cast oft die gleiche Syntax wie eine Konvertierung hat. Daher sollte die Frage lauten, wann eine Besetzung (implizit oder explizit) von der Sprache zugelassen wird und wann Sie eine (mehr) verwenden müssen. explizite Konvertierung?
Lassen Sie mich zunächst zeichnen eine einfache Linie zwischen ihnen. Formal (auch wenn dies für die Sprachsyntax gleichwertig ist) ändert eine Besetzung den Typ, während eine Konvertierung den Wert ändert (möglicherweise zusammen mit dem Typ). Auch ein Cast ist reversibel, während die Konvertierung möglicherweise nicht möglich ist.
Dieses Thema ist ziemlich umfangreich. Versuchen wir also, ein wenig einzugrenzen, indem wir benutzerdefinierte Darsteller aus dem Spiel ausschließen.
Implizite Besetzungen
In C # ist eine Umwandlung implizit, wenn Sie keine Informationen verlieren (bitte beachten Sie, dass diese Prüfung mit Typen und nicht mit ihren tatsächlichen Werten durchgeführt wird ).
Primitive Typen
Beispielsweise:
Diese Casts sind implizit, da Sie während der Konvertierung keine Informationen verlieren (Sie machen den Typ nur breiter). Umgekehrt ist implizite Umwandlung nicht zulässig, da Sie bei der Konvertierung möglicherweise Informationen verlieren, unabhängig von ihren tatsächlichen Werten (da sie nur zur Laufzeit überprüft werden können). Zum Beispiel wird dieser Code nicht kompiliert, weil a
double
möglicherweise einen Wert enthält (und tatsächlich tut), der nicht mit a dargestellt werden kannfloat
:Objekte
Im Fall eines Objekts (eines Zeigers auf) ist die Umwandlung immer implizit, wenn der Compiler sicher sein kann, dass der Quelltyp eine abgeleitete Klasse ist (oder den Typ der Zielklasse implementiert), zum Beispiel:
In diesem Fall weiß der Compiler , dass
string
implementiert wirdIFormattable
und dasNotSupportedException
heißt (abgeleitet von),Exception
so dass die Umwandlung implizit ist. Es gehen keine Informationen verloren, da Objekte ihren Typ nicht ändern (dies unterscheidet sich vonstruct
s und primitiven Typen, da Sie mit einer Besetzung ein neues Objekt eines anderen Typs erstellen ). Was sich ändert, ist Ihre Ansicht von ihnen.Explizite Besetzungen
Eine Umwandlung ist explizit, wenn die Konvertierung nicht implizit vom Compiler durchgeführt wird und Sie dann den Besetzungsoperator verwenden müssen. Normalerweise bedeutet dies:
Primitive Typen
Für primitive Typen ist eine explizite Umwandlung erforderlich, wenn Sie während der Konvertierung einige Daten verlieren können, zum Beispiel:
In beiden Beispielen gehen
float
Informationen verloren (in diesem Fall Genauigkeit) , selbst wenn die Werte in den Bereich fallen, sodass die Konvertierung explizit sein muss. Versuchen Sie jetzt Folgendes:Diese Konvertierung schlägt fehl. Sie muss also explizit sein, damit Sie sich dessen bewusst sind und eine Überprüfung durchführen können (im Beispiel ist der Wert konstant, er kann jedoch aus einigen Laufzeitberechnungen oder E / A stammen). Zurück zu Ihrem Beispiel:
Dies wird nicht kompiliert, da der Compiler keinen Text in Zahlen konvertieren kann. Text kann beliebige Zeichen enthalten, nicht nur Zahlen, und dies ist in C # zu viel, selbst für eine explizite Besetzung (aber möglicherweise in einer anderen Sprache zulässig).
Objekte
Die Konvertierung von Zeigern (in Objekte) kann fehlschlagen, wenn Typen keine Beziehung zueinander haben. Beispielsweise wird dieser Code nicht kompiliert (da der Compiler weiß, dass keine Konvertierung möglich ist):
Dieser Code wird kompiliert , aber es kann zur Laufzeit nicht (es hängt von der effektiven Art der gegossenen Objekte) mit einem
InvalidCastException
:Konvertierungen
Also, wenn Casts Konvertierung sind, warum brauchen wir dann Klassen wie
Convert
? Ignorieren Sie subtile Unterschiede, die sich aus derConvert
Implementierung und denIConvertible
Implementierungen ergeben, weil Sie in C # mit einer Besetzung dem Compiler sagen:-oder-
Für alles andere eine weitere explizite Operation erforderlich (man denke über Auswirkungen der einfachen Würfe , deshalb C ++ lange eingeführt, ausführliche und explizite Syntax für sie). Dies kann eine komplexe Operation beinhalten (für
string
->double
Konvertierung ist eine Analyse erforderlich). Eine Konvertierung instring
zum Beispiel ist immer möglich (über eineToString()
Methode), kann jedoch etwas anderes bedeuten als erwartet. Daher muss es expliziter sein als eine Besetzung ( mehr Sie schreiben, mehr denken Sie darüber nach, was Sie tun ).Diese Konvertierung kann innerhalb des Objekts (unter Verwendung bekannter IL-Anweisungen dafür) unter Verwendung von benutzerdefinierten Konvertierungsoperatoren (die in der zu konvertierenden Klasse definiert sind) oder komplexeren Mechanismen (
TypeConverter
z. B. oder Klassenmethoden) erfolgen. Sie wissen nicht, was passieren wird, aber Sie wissen, dass dies möglicherweise fehlschlägt (deshalb sollten Sie IMO verwenden, wenn eine kontrollierte Konvertierung möglich ist). In Ihrem Fall einfach die Konvertierung analysiert diestring
ein zu produzierendouble
:Dies kann natürlich fehlschlagen. Wenn Sie dies tun, sollten Sie immer die Ausnahme abfangen, die es möglicherweise auslöst (
FormatException
). Es ist hier nicht zum Thema, aber wenn aTryParse
verfügbar ist, sollten Sie es verwenden (weil Sie semantisch sagen, dass es möglicherweise keine Zahl ist und es sogar noch schneller ist ... zu scheitern).Konvertierungen in .NET können von vielen Stellen stammen,
TypeConverter
implizite / explizite Casts mit benutzerdefinierten Konvertierungsoperatoren, ImplementierungIConvertible
und Analyse von Methoden (habe ich etwas vergessen?). Weitere Informationen hierzu finden Sie in MSDN.Um diese lange Antwort zu beenden, nur ein paar Worte zu benutzerdefinierten Konvertierungsoperatoren. Es ist nur Zucker , den Programmierer einen Cast verwenden zu lassen, um einen Typ in einen anderen umzuwandeln. Es ist eine Methode innerhalb einer Klasse (die, die gewirkt wird), die besagt: "Hey, wenn er / sie diesen Typ in diesen Typ konvertieren möchte, kann ich es tun." Beispielsweise:
In diesem Fall ist dies explizit, da es möglicherweise fehlschlägt, dies jedoch der Implementierung überlassen wird (auch wenn diesbezüglich Richtlinien bestehen). Stellen Sie sich vor, Sie schreiben eine benutzerdefinierte Zeichenfolgenklasse wie folgt:
In Ihrer Implementierung können Sie entscheiden, "das Leben des Programmierers zu vereinfachen" und diese Konvertierung über eine Besetzung verfügbar zu machen (denken Sie daran, dass dies nur eine Abkürzung ist, um weniger zu schreiben). Einige Sprachen erlauben dies möglicherweise sogar:
Ermöglichen der impliziten Konvertierung in einen beliebigen Typ (Überprüfung wird zur Laufzeit durchgeführt). Mit geeigneten Optionen kann dies beispielsweise in VB.NET erfolgen. Es ist nur eine andere Philosophie.
Was kann ich mit ihnen machen?
Die letzte Frage ist also, wann Sie die eine oder andere verwenden sollten. Mal sehen, wann Sie eine explizite Besetzung verwenden können:
object
zu einem anderen Typ (dies kann auch das Unboxing einschließen).Es kann nur die erste Konvertierung durchgeführt werden,
Convert
sodass Sie für die anderen keine Wahl haben und eine explizite Besetzung verwenden müssen.Mal sehen, wann Sie verwenden können
Convert
:IConvertible
in einen anderen (unterstützten) Typ implementiert wird .byte
Array zu / von einer Zeichenfolge.Schlussfolgerungen
IMO
Convert
sollte jedes Mal verwendet werden, wenn Sie wissen, dass eine Konvertierung fehlschlägt (aufgrund des Formats, des Bereichs oder weil sie möglicherweise nicht unterstützt wird), selbst wenn dieselbe Konvertierung mit einer Besetzung durchgeführt werden kann (sofern nichts anderes verfügbar ist). Es macht deutlich, wer Ihren Code lesen wird, was Ihre Absicht ist und dass er möglicherweise fehlschlägt (Vereinfachung des Debugs).Für alles andere brauchen Sie eine Besetzung, keine Wahl, aber wenn eine andere bessere Methode verfügbar ist, empfehle ich Ihnen, sie zu verwenden. In Ihrem Beispiel ist eine Konvertierung von
string
nachdouble
etwas, das (insbesondere wenn Text vom Benutzer stammt) sehr oft fehlschlägt. Sie sollten es daher so deutlich wie möglich machen (außerdem erhalten Sie mehr Kontrolle darüber), beispielsweise mithilfe einerTryParse
Methode.Edit: Was ist der Unterschied zwischen ihnen?
Entsprechend der aktualisierten Frage und der Beibehaltung dessen, was ich zuvor geschrieben habe (etwa wann Sie eine Besetzung verwenden können, im Vergleich zu dem Zeitpunkt, zu dem Sie sie verwenden können / müssen
Convert
), ist der letzte Punkt, der geklärt werden muss, ob es Unterschiede zwischen ihnen gibt (außerdemConvert
VerwendungenIConvertible
undIFormattable
Schnittstellen, damit Operationen ausgeführt werden können) nicht erlaubt mit Abgüssen).Kurze Antwort ist ja, sie verhalten sich anders . Ich sehe die
Convert
Klasse so oft wie eine Hilfsmethodenklasse, dass sie Vorteile oder leicht unterschiedliche Verhaltensweisen bietet . Beispielsweise:Ganz anders, oder? Cast-Kürzungen (das erwarten wir alle) führen jedoch
Convert
eine Rundung auf die nächste Ganzzahl durch (und dies ist möglicherweise nicht zu erwarten, wenn Sie sich dessen nicht bewusst sind). Jede Konvertierungsmethode führt zu Unterschieden, sodass eine allgemeine Regel nicht angewendet werden kann und sie von Fall zu Fall angezeigt werden müssen. 19 Basistypen zum Konvertieren in jeden anderen Typ ... Liste kann ziemlich lang sein, viel besser, um MSDN von Fall zu Fall zu konsultieren Fall!quelle
Difference between casting and using the Convert.To() method
. Ansonsten sehr umfassende Antwort. (Ich hoffe, meine Frage wird wieder geöffnet ...)double
nicht, dass Werte, die keine ganzen Zahlen darstellen, in "konvertierbar" sein solltenint
. Ein Abguss würde das entsprechende Paradigma in Fällen scheint , wo zB einen abruftInt32
Wert von a ,double[]
die eine Mischung aus reellen Zahlen und hältInt32
Werten, die umgewandelt wurdedouble
[ein Versuch , einen Wert zu konvertieren , die nicht darstellbare ist gerade inint32
einen unerwarteten Zustand anzeigen würde , und sollte eine Ausnahme auslösen], aber ich würde denken, wenn man eine verlustbehaftete Konvertierung will, sollte man spezifisch über die Form sein, die man will.object o = 123; var l = Convert.ToInt64(o); var i = (long) (int) o; var f = (long) o // InvalidCastException
float
->int
), sondern ein Zwang . Eine Besetzung könnte zum Beispiel seinDerivedClass
->BaseClass
. Es ist verwirrend, weil wir in C # für beide dasselbe Wort (und denselben Operator) verwenden, aber es handelt sich tatsächlich um unterschiedliche Dinge. Eine formale Definition zur Unterscheidung ist etwas komplizierter als das, was ich geschrieben habe.Casting ist eine Möglichkeit, dem Compiler zu sagen: "Ich weiß, dass Sie denken, dass diese Variable ein Balken ist, aber ich weiß zufällig mehr als Sie. Das Objekt ist tatsächlich ein Foo. Lassen Sie mich es also so behandeln, als wäre es ein Foo von." jetzt weiter. " Wenn sich dann zur Laufzeit herausstellt, dass das eigentliche Objekt wirklich ein Foo ist, funktioniert Ihr Code. Wenn sich herausstellt, dass das Objekt überhaupt kein Foo war, erhalten Sie eine Ausnahme. (Speziell ein
System.InvalidCastException
.)Das Konvertieren ist eine Möglichkeit zu sagen: "Wenn Sie mir ein Objekt vom Typ Bar geben, kann ich ein brandneues Foo-Objekt erstellen, das darstellt, was sich in diesem Bar-Objekt befindet. Ich werde das ursprüngliche Objekt nicht ändern, es hat gewonnen." Wenn Sie das ursprüngliche Objekt nicht anders behandeln, wird etwas Neues erstellt, das nur auf einem anderen Wert basiert . Wie es das tun wird, kann alles sein. Im Falle eines
Convert.ToDouble
Aufrufs wird es am Ende aufgerufenDouble.Parse
Das hat alle Arten von komplexer Logik, um zu bestimmen, welche Arten von Zeichenfolgen welche numerischen Werte darstellen. Sie können Ihre eigene Konvertierungsmethode schreiben, mit der Zeichenfolgen unterschiedlich doppelt zugeordnet werden (möglicherweise, um eine völlig andere Konvention für die Anzeige von Zahlen zu unterstützen, z. B. römische Ziffern oder was auch immer). Eine Konvertierung kann alles bewirken, aber die Idee ist, dass Sie den Compiler nicht wirklich bitten, etwas für Sie zu tun. Sie sind derjenige, der den Code schreibt, um zu bestimmen, wie das neue Objekt erstellt werden soll, da der Compiler ohne Ihre Hilfe nicht weiß, wie er (als Beispiel) astring
einem zuordnen kanndouble
.Also, wann konvertierst du und wann wirfst du? In beiden Fällen haben wir eine Variable vom Typ A, sagen wir A, und wir möchten eine Variable vom Typ B. Wenn unser A-Objekt tatsächlich unter der Haube ein B ist, dann werfen wir. Wenn es nicht wirklich ein B ist, müssen wir es konvertieren und definieren, wie das Programm ein B von einem A erhalten soll.
quelle
foreach
). Außerhalb dieser Ausnahmen sind Abgüsse per Definition explizit.Die
Convert.Double
Methode ruft dieDouble.Parse(string)
Methode eigentlich nur intern auf .Weder der
String
Typ noch derDouble
Typ definieren eine explizite / implizite Konvertierung zwischen den beiden Typen, sodass das Casting immer fehlschlägt.Die
Double.Parse
Methode betrachtet jedes Zeichen in derstring
und erstellt einen numerischen Wert basierend auf den Werten der Zeichen in derstring
. Wenn eines der Zeichen ungültig ist,Parse
schlägt die Methode fehl (was dazu führt, dass dieConvert.Double
Methode ebenfalls fehlschlägt).quelle
Convert.ToDouble()
Blicks über Bytes hinaus die Daten berücksichtigen?In Ihrem Beispiel versuchen Sie, eine Zeichenfolge in ein Double umzuwandeln (nicht integraler Typ).
Eine explizite Konvertierung ist erforderlich, damit es funktioniert.
Und ich muss darauf hinweisen, dass Sie
Convert.ToDouble
stattdessen hätten verwendenConvert.ToInt64
können, da Sie die Bruchteile des doppelten Werts verlieren können, wenn Sie in ein int konvertieren.Wenn Ihre Variable den Wert "5.25" hat, wäre varDouble 5.00 gewesen (Verlust von 0.25 aufgrund der Konvertierung in Int64).
Um Ihre Frage zu Casting vs Converting zu beantworten.
Ihre Besetzung (eine explizite Besetzung) erfüllt nicht die Anforderungen für eine explizite Besetzung. Der Wert, den Sie mit dem Cast-Operator umwandeln möchten, ist ungültig (dh nicht ganzzahlig).
Auf dieser MSDN-Seite finden Sie die Regeln für Casting / Conversions
quelle
Das Casting beinhaltet keine Konvertierung, dh die interne Darstellung eines Wertes wird nicht geändert. Beispiel:
Sie können konvertieren eine
double
zustring
dieser magSie können a
string
aufdouble
verschiedene Arten in a konvertieren (hier nur zwei davon):Oder auf sichere Weise
quelle
Convert.ToDouble()
intern anruftDouble.Parse()
. Ist es zu meinem Vorteil,Convert.ToDouble()
über zu verwendenDouble.Parse()
oder nicht und warum?Convert.ToDouble
hat viele Überladungen, die verschiedene Arten von Eingaben akzeptieren. Die Überlastung der Annahmestring
zurückkehrt ,0.0
wenn einenull
Zeichenfolge übergeben wird. Abgesehen davon sehe ich keinen Vorteil darin, es zu benutzen.Double.Parse()
etwas zu bieten, das ich berücksichtigen sollte?Double.Parse()
ist direkter alsConvert.ToDouble()
. Wenn Sie sicher sind, dass Ihre Zeichenfolge eine gültige Nummer enthält, können Sie diese sicher verwenden. Andernfalls empfehle ich Ihnen, sie zu verwendenDouble.TryParse
.Von
MSDN
:Betrachten Sie das folgende Beispiel:
Und auch:
Sie können
System.Convert
class verwenden, wenn Sie zwischen nicht kompatiblen Typen konvertieren möchten . Der Hauptunterschied zwischen Casting und Konvertierung besteht in Kompilierung und Laufzeit . Die Ausnahmen für die Typkonvertierung werden zur Laufzeit angezeigt . Das heißt, eine zur Laufzeit fehlgeschlagene Typumwandlung führt dazu, dass eineInvalidCastException
ausgelöst wird.Fazit: Beim Casting teilen Sie dem Compiler mit, dass
a
es sich wirklich um einen Typ handelt,b
und wenn ja, wird das Projekt ohne Fehler wie in diesem Beispiel erstellt:Aber bei der Konvertierung, die Sie dem Compiler sagen, gibt es eine Möglichkeit, ein neues Objekt vom
a
Typ zu erstellen. Führenb
Sie dies aus und erstellen Sie Projekte ohne Fehler. Wie ich bereits sagte, wenn die Typumwandlung zur Laufzeit fehlschlägt, führt dies zu einemInvalidCastException
to geworfen werden .Zum Beispiel wird der folgende Code niemals kompiliert, da der Compiler erkennt, dass ein Ausdruck vom Typ nicht
DateTime
in einen Typ umgewandelt werden kannint
:Aber dieser ist erfolgreich kompiliert:
Aber zur Laufzeit erhalten Sie
InvalidCastException
Folgendes:quelle
Die oben genannte Konvertierung ist von der Sprache einfach nicht zulässig. Hier ist eine Liste expliziter Umwandlungen für numerische Typen: http://msdn.microsoft.com/en-us/library/yht2cx7b.aspx Wie Sie sehen, konnte nicht jeder numerische Typ in einen anderen numerischen Typ konvertiert werden
Weitere Infos zum Casting hier
Wenn Sie einen Typ umwandeln, wird die Datenstruktur nicht geändert. Nun, im Falle einer Konvertierung numerischer Werte können Sie einige Bits verlieren oder einige zusätzliche 0 Bits erhalten. Aber Sie arbeiten immer noch mit einer Nummer. Sie ändern nur die von dieser Nummer belegte Speichermenge. Das ist sicher genug, damit der Compiler alles Notwendige tun kann.
Wenn Sie jedoch versuchen, eine Zeichenfolge in eine Zahl umzuwandeln, können Sie dies nicht tun, da es nicht ausreicht, die von der Variablen belegte Speichermenge zu ändern. Zum Beispiel 5.00als Zeichenfolge ist eine Folge von "Zahlen": 53 (5) 46 (.) 48 (0) 48 (0) - das ist für ASCII, aber die Zeichenfolge enthält etwas Ähnliches. Wenn der Compiler nur die ersten N (4 für doppelt? Nicht sicher) Bytes aus einer Zeichenfolge nimmt, enthält dieses Stück eine völlig andere doppelte Zahl. Gleichzeitig führt Convert.ToDouble () einen speziellen Algorithmus aus, der jedes Symbol einer Zeichenfolge verwendet, die Ziffer herausfindet, die es darstellt, und eine doppelte Zahl für Sie erstellt, wenn die Zeichenfolge eine Zahl darstellt. Sprachen wie PHP rufen im Hintergrund ungefähr Convert.ToDouble für Sie auf. Aber C # wird das wie eine statisch typisierte Sprache nicht für Sie tun. Auf diese Weise können Sie sicher sein, dass jede Operation typsicher ist und Sie nichts Unerwartetes erhalten, wenn Sie Folgendes tun:
quelle
Das Umwandeln eines Strings in ein solches Double ist nicht zulässig. C # Aus diesem Grund erhalten Sie eine Ausnahme. Sie müssen den String konvertieren lassen ( MSDN-Dokument , das akzeptable Konvertierungspfade anzeigt). Dies liegt einfach daran, dass eine Zeichenfolge nicht unbedingt numerische Daten enthalten muss, sondern die verschiedenen numerischen Typen (mit Ausnahme von Nullwerten). EIN
Convert
führt eine Methode aus, die die Zeichenfolge überprüft, um festzustellen, ob sie in einen numerischen Wert umgewandelt werden kann. Wenn dies möglich ist, wird dieser Wert zurückgegeben. Wenn dies nicht möglich ist, wird eine Ausnahme ausgelöst.Um es zu konvertieren, haben Sie mehrere Möglichkeiten. Sie haben die
Convert
Methode in Ihrer Frage verwendet,Parse
die weitgehend ähnlich istConvert
, aber Sie sollten sich auch TryParse ansehen, mit der Sie Folgendes tun können:Dies vermeidet die mögliche Ausnahme sollten Sie versuchen,
Convert
oderParse
eine nicht-numerische Zeichenfolge.quelle
TryParse
Over zu verwenden,Convert
weilTryParse
überprüft wird, ob die Konvertierung erfolgreich ist?double varDouble = (double)variable
geht davon aus, dassvariable
das schon ein Doppel ist. Wennvariable
es sich nicht um ein Double handelt (es handelt sich um eine Zeichenfolge), schlägt dies fehl.double varDouble = Convert.ToDouble(variable)
mag es sagt - es konvertiert. Wenn es ein Double analysieren oder auf andere Weise extrahieren kannvariable
kann, wird es.Ich benutze
Double.Parse
oderDouble.TryParse
weil es deutlicher anzeigt, was passieren soll. Sie beginnen mit einer Zeichenfolge und erwarten, dass diese in eine doppelte konvertierbar ist. Wenn Sie Zweifel haben, verwenden SieTryParse
.Wenn
variable
es sich um ein Methodenargument handelt, ändern Sie den Typ in double. Machen Sie den Anrufer für die Bereitstellung des richtigen Typs verantwortlich. Auf diese Weise erledigt der Compiler die Arbeit für Sie.quelle
Der wichtigste Unterschied ist , dass , wenn Typumwandlung verwendet wird , und die Konvertierung fehlschlägt (sagt wir einen sehr großen Schwimmer Wert int Umwandlung) keine Ausnahme ausgelöst und ein int des Minimalwert gezeigt , werden wird halten. Bei Verwendung von Convert wird für solche Szenarien jedoch eine Ausnahme ausgelöst.
quelle