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:
Beim Casting wird eine Entität eines Datentyps in eine andere geändert.
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:
int tinyInteger = 10;
long bigInteger = tinyInteger;
float tinyReal = 10.0f;
double bigReal = tinyReal;
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 kann float
:
// won't compile!
double bigReal = Double.MaxValue;
float tinyReal = bigReal;
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:
string text = "123";
IFormattable formattable = text;
NotSupportedException derivedException = new NotSupportedException();
Exception baseException = derivedException;
In diesem Fall weiß der Compiler , dass string
implementiert wird IFormattable
und das NotSupportedException
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 von struct
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:
- Sie können Informationen oder Daten verlieren, daher müssen Sie sich dessen bewusst sein.
- Die Konvertierung schlägt möglicherweise fehl (da Sie einen Typ nicht in den anderen konvertieren können). Sie müssen also erneut wissen, was Sie tun.
Primitive Typen
Für primitive Typen ist eine explizite Umwandlung erforderlich, wenn Sie während der Konvertierung einige Daten verlieren können, zum Beispiel:
double precise = Math.Cos(Math.PI * 1.23456) / Math.Sin(1.23456);
float coarse = (float)precise;
float epsilon = (float)Double.Epsilon;
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:
float max = (float)Double.MaxValue;
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:
// won't compile!
string text = "123";
double value = (double)text;
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):
// won't compile!
string text = (string)AppDomain.Current;
Exception exception = (Exception)"abc";
Dieser Code wird kompiliert , aber es kann zur Laufzeit nicht (es hängt von der effektiven Art der gegossenen Objekte) mit einem InvalidCastException
:
object obj = GetNextObjectFromInput();
string text = (string)obj;
obj = GetNextObjectFromInput();
Exception exception = (Exception)obj;
Konvertierungen
Also, wenn Casts Konvertierung sind, warum brauchen wir dann Klassen wie Convert
? Ignorieren Sie subtile Unterschiede, die sich aus der Convert
Implementierung und den IConvertible
Implementierungen ergeben, weil Sie in C # mit einer Besetzung dem Compiler sagen:
Vertrauen Sie mir, dieser Typ ist dieser Typ, auch wenn Sie ihn jetzt nicht kennen, lassen Sie es mich tun und Sie werden sehen.
-oder-
Keine Sorge, es ist mir egal, dass bei dieser Konvertierung etwas verloren geht.
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 in string
zum Beispiel ist immer möglich (über eine ToString()
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 die string
ein zu produzieren double
:
double value = Double.Parse(aStringVariable);
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 a TryParse
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, Implementierung IConvertible
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:
float? maybe = 10; // Equals to Nullable<float> maybe = 10;
float sure1 = (float)maybe; // With cast
float sure2 = maybe.Value; // Without cast
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:
EasyString text = "123"; // Implicit from string
double value = (string)text; // Explicit to double
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:
double value = "123";
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:
- Konvertierungen zwischen Basistypen.
- Konvertierungen von
object
zu einem anderen Typ (dies kann auch das Unboxing einschließen).
- Konvertierungen von einer abgeleiteten Klasse in eine Basisklasse (oder in eine implementierte Schnittstelle).
- Konvertierungen von einem Typ in einen anderen über benutzerdefinierte Konvertierungsoperatoren.
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
:
- Konvertierungen von einem beliebigen Basistyp in einen anderen Basistyp (mit einigen Einschränkungen siehe MSDN ).
- Konvertierungen von jedem Typ, der
IConvertible
in einen anderen (unterstützten) Typ implementiert wird .
- Konvertierungen von / zu einem
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
nach double
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 einer TryParse
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ßerdem Convert
Verwendungen IConvertible
und IFormattable
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:
double real = 1.6;
int castedInteger = (int)real; // 1
int convertedInteger = Convert.ToInt32(real); // 2
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!