Double.TryParse oder Convert.ToDouble - was ist schneller und sicherer?


80

Meine Anwendung liest eine Excel-Datei mit VSTO und fügt die gelesenen Daten zu a hinzu StringDictionary. Es werden nur Daten hinzugefügt, bei denen es sich um Zahlen mit wenigen Ziffern handelt (1000 1000,2 1000,34 - Komma ist ein Trennzeichen in russischen Standards).

Was ist besser, um zu überprüfen, ob die aktuelle Zeichenfolge eine geeignete Nummer ist?

object data, string key; // data had read

try
{
  Convert.ToDouble(regionData, CultureInfo.CurrentCulture);
  dic.Add(key, regionData.ToString());
}
catch (InvalidCastException)
{
  // is not a number
}

oder

double d;
string str = data.ToString();
if (Double.TryParse(str, out d)) // if done, then is a number
{
  dic.Add(key, str);
}

Ich muss StringDictionaryanstelle von Dictionary<string, double>wegen der folgenden Parsing-Algorithmus Probleme verwenden.

Meine Fragen: Welcher Weg ist schneller? Welches ist sicherer?

Und ist es besser anzurufen Convert.ToDouble(object)oder Convert.ToDouble(string)?


Zu Ihrer Information, double.TryParse ist dasselbe wie try {result = double.Parse (s); return true; } catch {return false; }. Convert ist im Wesentlichen ein Wrapper für beide mit einer Reihe von Überladungen. Es macht keinen Unterschied, wie Sie es tun. Aber wie Jon betont hat, überlegen Sie, wie Sie mit schlechten Eingaben umgehen sollen.
John Leidegren

12
Double.TryParse ist nicht dasselbe wie double.Parse, das in einen try..catch eingeschlossen ist. Die Semantik ist dieselbe, aber der Codepfad ist unterschiedlich. TryParse überprüft zunächst anhand einer internen Number.TryStringToNumber, ob es sich bei der Zeichenfolge um eine Zahl handelt, während Parse davon ausgeht, dass es sich bereits um eine Zahl / ein Doppel handelt.
Jeff Moser

Antworten:


131

Ich habe einen schnellen, nicht wissenschaftlichen Test im Release-Modus durchgeführt. Ich habe zwei Eingaben verwendet: "2.34523" und "badinput" in beide Methoden und 1.000.000 Mal iteriert.

Gültige Eingabe:

Double.TryParse = 646ms
Convert.ToDouble = 662 ms

Nicht viel anders als erwartet. Für gültige Eingaben sind diese in jeder Hinsicht gleich.

Ungültige Eingabe:

Double.TryParse = 612ms
Convert.ToDouble = ..

Nun ... es lief lange. Ich habe das Ganze mit 1.000 Iterationen wiederholt und Convert.ToDoublebei schlechten Eingaben 8,3 Sekunden gebraucht . Im Durchschnitt würde es über 2 Stunden dauern. Es ist mir egal, wie grundlegend der Test ist. Im ungültigen Eingabefall wird Convert.ToDoubledas Auslösen von Ausnahmen Ihre Leistung beeinträchtigen.

Also, hier ist eine weitere Abstimmung TryParsemit einigen Zahlen, um dies zu belegen.


4
Zusätzlich zu den oben genannten Dingen habe ich gerade festgestellt, dass Convert.ToDouble () eine Ausnahme mit Zahlen in wissenschaftlicher Notation auslöst. Beachten Sie Folgendes: double toDouble = Convert.ToDouble ((- 1/30000) .ToString ()); // schlägt fehl double dblParse = Double.Parse ((- 1/30000) .ToString ()); // funktioniert gut
Buddy Lee

46

Zunächst würde ich double.Parseeher als Convert.ToDoublein erster Linie verwenden.

Ob Sie verwenden sollten Parseoder TryParse: Können Sie fortfahren, wenn schlechte Eingabedaten vorliegen, oder ist dies eine wirklich außergewöhnliche Bedingung? Wenn es außergewöhnlich ist, verwenden ParseSie es und lassen Sie es explodieren, wenn die Eingabe schlecht ist. Wenn es erwartet wird und sauber gehandhabt werden kann, verwenden Sie TryParse.


6
Jon, können Sie näher erläutern, warum Sie double.Parse gegenüber Convert.ToDouble bevorzugen?
David North

10
@dnorthut: Ich möchte selten, dass null in 0 konvertiert wird (was Convert.ToDouble tut). Es ist auch im Allgemeinen flexibler. Ich neige nur dazu, mich für die spezifischen Methoden zu entscheiden ...
Jon Skeet

8

In den Entwurfsrichtlinien für .NET Framework wird die Verwendung der Try-Methoden empfohlen. Ausnahmen zu vermeiden ist normalerweise eine gute Idee.

Convert.ToDouble(object) Wird besorgt ((IConvertible) object).ToDouble(null);

Welches wird anrufen Convert.ToDouble(string, null)

Es ist also schneller, die String-Version aufzurufen.

Die String-Version macht jedoch nur Folgendes:

if (value == null)
{
    return 0.0;
}
return double.Parse(value, NumberStyles.Float | NumberStyles.AllowThousands, provider);

So geht es schneller double.Parsedirekt.


7

Wenn Sie die Ausnahme nicht behandeln möchten, wählen Sie TryParse. TryParse ist schneller, da es nicht den gesamten Exception-Stack-Trace verarbeiten muss.


7

Ich versuche im Allgemeinen, die ConvertKlasse zu meiden (was bedeutet: Ich benutze sie nicht), weil ich sie sehr verwirrend finde: Der Code gibt zu wenig Hinweise darauf, was genau hier passiert, da Convertviele semantisch sehr unterschiedliche Konvertierungen mit demselben Code möglich sind . Dies macht es für den Programmierer schwierig zu kontrollieren, was genau passiert.

Mein Rat ist daher, diese Klasse niemals zu verwenden. Es ist auch nicht wirklich notwendig (außer für die binäre Formatierung einer Zahl, da die normale ToStringMethode der Zahlenklassen keine geeignete Methode dafür bietet).


7

Sofern Sie nicht zu 100% sicher sind, was selten der Fall ist, sollten Sie Double.TryParse verwenden.

Convert.ToDouble will throw an exception on non-numbers
Double.Parse will throw an exception on non-numbers or null
Double.TryParse will return false or 0 on any of the above without generating an exception.

Die Geschwindigkeit der Analyse wird zweitrangig, wenn Sie eine Ausnahme auslösen, da es nicht viel langsamer als eine Ausnahme gibt.


4

Viel Hass für die Convert-Klasse hier ... Nur um ein wenig auszugleichen, gibt es einen Vorteil für Convert - wenn Sie ein Objekt erhalten,

Convert.ToDouble(o);

kann den Wert einfach zurückgeben, wenn o bereits ein Double ist (oder ein int oder etwas leicht umsetzbares).

Die Verwendung von Double.Parse oder Double.TryParse ist großartig, wenn Sie es bereits in einer Zeichenfolge haben, aber

Double.Parse(o.ToString());

muss gehen, um die Zeichenfolge zuerst zu analysieren und abhängig von Ihrer Eingabe, die teurer sein könnte.


1
+1: Ich sehe Ihren Standpunkt, das Parsen von Zahlen / Zeichenfolgen in Boxen mit Zahlen in einem System. Das Objekt ist ziemlich einfach, stattdessen eine massive Typprüfung. Aber im Grunde stimme ich anderen Antworten zu: Convert.ToSomething()ist so viel teurer als Parse / TryParse, besonders in einem Iterationskontext
T-moty

Für mich hat Convert.ToDouble (o) ein paar einfache Outs, wenn das, was in der Box ist, bereits eine Zahl ist, aber der wahre Killer für Convert ist, dass es kein .TryToDouble (o, out d) hat; Angesichts der Tatsache, wie teuer Ausnahmen sind (und wie sicher Sie von den Eingaben sind oder nicht), ist dies der große Mehraufwand von Convert.
user1664043

2

Double.TryParse IMO.

Es ist einfacher für Sie zu handhaben. Sie wissen genau, wo der Fehler aufgetreten ist.

Dann können Sie damit umgehen, wie Sie es für richtig halten, wenn es false zurückgibt (dh nicht konvertieren konnte).


2

Ich habe es immer vorgezogen, die TryParse()Methoden zu verwenden, weil sie den Erfolg oder Misserfolg der Konvertierung zurückspucken werden, ohne sich um Ausnahmen sorgen zu müssen.


2

Dies ist eine interessante alte Frage. Ich füge eine Antwort hinzu, weil niemand ein paar Dinge mit der ursprünglichen Frage bemerkt hat.

Was ist schneller: Convert.ToDouble oder Double.TryParse? Was ist sicherer: Convert.ToDouble oder Double.TryParse?

Ich werde diese beiden Fragen im Detail beantworten (ich werde die Antwort später aktualisieren), aber zuerst:

Aus Sicherheitsgründen hat jeder Programmierer in dieser Frage die Zeile (Hervorhebung von mir) übersehen:

Es werden nur Daten hinzugefügt, bei denen es sich um Zahlen mit wenigen Ziffern handelt (1000 1000,2 1000,34 - Komma ist ein Trennzeichen in russischen Standards ).

Gefolgt von diesem Codebeispiel:

Convert.ToDouble(regionData, CultureInfo.CurrentCulture);

Interessant ist hier: Wenn die Tabellen im russischen Zahlenformat vorliegen, Excel die Zellenfelder jedoch nicht korrekt eingegeben hat, wie werden die aus Excel eingegebenen Werte richtig interpretiert?

Hier ist eine weitere interessante Sache zu den beiden Beispielen in Bezug auf die Geschwindigkeit:

catch (InvalidCastException)
{
    // is not a number
}

Dies wird wahrscheinlich MSIL erzeugen, das so aussieht:

catch [mscorlib]System.InvalidCastException 
{
  IL_0023:  stloc.0
  IL_0024:  nop
  IL_0025:  ldloc.0
  IL_0026:  nop
  IL_002b:  nop
  IL_002c:  nop
  IL_002d:  leave.s    IL_002f
}  // end handler
IL_002f: nop
IL_0030: return

In diesem Sinne können wir wahrscheinlich die Gesamtzahl der von jedem Programm ausgeführten MSIL-Anweisungen vergleichen - dazu später mehr, wenn ich diesen Beitrag aktualisiere.

Ich glaube, Code sollte korrekt, klar und schnell sein ... In dieser Reihenfolge!


1

Persönlich finde ich die TryParseMethode leichter zu lesen. Welche Sie tatsächlich verwenden möchten, hängt von Ihrem Anwendungsfall ab: Wenn Fehler lokal behandelt werden können, erwarten Sie Fehler und ein Bool von TryParseist gut, andernfalls möchten Sie vielleicht einfach lassen Die Ausnahmen fliegen.

Ich würde erwarten, dass das TryParseauch schneller ist, da es den Aufwand für die Ausnahmebehandlung vermeidet. Verwenden Sie jedoch ein Benchmark-Tool wie die MiniBench von Jon Skeet , um die verschiedenen Möglichkeiten zu vergleichen.


Feedback zu dieser Antwort: Wenn in den Fragen gefragt wird, was schneller und sicherer ist, sollte die Antwort nicht mit "Persönlich" beginnen und Vermutungen wie "Ich würde erwarten ..." enthalten. Dies sind nur persönliche Gedanken und Kommentare, keine gute Antwort.
PandaWood
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.