Double.TryParse lub Convert.ToDouble - co jest szybsze i bezpieczniejsze?

80

Moja aplikacja odczytuje plik Excela za pomocą VSTO i dodaje odczytane dane do pliku StringDictionary. Dodaje tylko dane, które są liczbami z kilkoma cyframi (1000 1000,2 1000,34 - przecinek jest separatorem w rosyjskich standardach).

Co lepiej sprawdzić, czy bieżący ciąg jest odpowiednią liczbą?

object data, string key; // data had read

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

lub

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

Muszę używać StringDictionaryzamiast z Dictionary<string, double>powodu następujących problemów z algorytmem analizowania.

Moje pytania: Która droga jest szybsza? Który jest bezpieczniejszy?

A czy lepiej zadzwonić Convert.ToDouble(object)czy Convert.ToDouble(string)?

abatishchev
źródło
FYI, double.TryParse to to samo co try {result = double.Parse (s); powrót prawda; } catch {return false; }. Convert jest w zasadzie opakowaniem dla obu z wieloma przeciążeniami. Nie ma znaczenia, jak to robisz. Ale jak wskazał Jon, zastanów się, jak radzić sobie ze złymi danymi wejściowymi.
John Leidegren,
12
Double.TryParse to nie to samo, co double.Parse opakowane w try..catch. Semantyka jest taka sama, ale ścieżka kodu jest inna. TryParse najpierw sprawdza, czy ciąg jest liczbą przy użyciu wewnętrznego Number.TryStringToNumber, podczas gdy Parse zakłada, że ​​jest to już liczba / double.
Jeff Moser,

Odpowiedzi:

131

Zrobiłem szybki nienaukowy test w trybie wydania. Użyłem dwóch danych wejściowych: „2.34523” i „badinput” w obu metodach i wykonałem iterację 1 000 000 razy.

Prawidłowe dane wejściowe:

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

Niewiele się różniło, zgodnie z oczekiwaniami. Dla wszystkich zamiarów i celów są one takie same.

Nieprawidłowe dane wejściowe:

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

No cóż ... długo to działało. Całość przeprowadziłem ponownie, używając 1000 iteracji i Convert.ToDoubleprzy złych danych wejściowych zajęło to 8,3 sekundy. Uśrednianie zajmie ponad 2 godziny. Nie obchodzi mnie, jak podstawowy jest test, w przypadku nieprawidłowych danych wejściowych Convert.ToDoublezgłoszenie wyjątku zrujnuje wydajność.

Oto kolejny głos za TryParsez pewnymi liczbami, aby to potwierdzić .

Szymon Rozga
źródło
4
Oprócz rzeczy wspomnianych powyżej, właśnie odkryłem, że Convert.ToDouble () zgłosi wyjątek z liczbami w notacji naukowej. Rozważ to: double toDouble = Convert.ToDouble ((- 1/30000) .ToString ()); // zakończy się niepowodzeniem double dblParse = Double.Parse ((- 1/30000) .ToString ()); // działa dobrze
Buddy Lee
46

Na początek użyłbym double.Parseraczej niż Convert.ToDoublew pierwszej kolejności.

Co do tego, czy powinieneś użyć, Parseczy TryParse: czy możesz kontynuować, jeśli są złe dane wejściowe, czy też jest to naprawdę wyjątkowy stan? Jeśli jest wyjątkowy, użyj Parsei pozwól mu wybuchnąć, jeśli wejście jest złe. Jeśli jest to oczekiwane i można je czysto obchodzić, użyj TryParse.

Jon Skeet
źródło
6
Jon, czy możesz wyjaśnić, dlaczego wolisz double.Parse zamiast Convert.ToDouble?
David North,
10
@dnorthut: Rzadko chcę, aby wartość null była konwertowana na 0 (co w zasadzie robi Convert.ToDouble). Jest też ogólnie bardziej elastyczny. Po prostu staram się wybierać konkretne metody ...
Jon Skeet
8

Wytyczne projektowe .NET Framework zalecają użycie metod Try. Unikanie wyjątków jest zwykle dobrym pomysłem.

Convert.ToDouble(object) zrobi ((IConvertible) object).ToDouble(null);

Który zadzwoni Convert.ToDouble(string, null)

Więc szybciej jest wywołać wersję string.

Jednak wersja string robi to:

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

Więc szybciej jest zrobić to double.Parsebezpośrednio.

Jeff Moser
źródło
7

Jeśli nie zamierzasz obsługiwać wyjątku, przejdź do TryParse. TryParse jest szybszy, ponieważ nie musi zajmować się całym śladem stosu wyjątków.

Aaron Palmer
źródło
7

Generalnie staram się unikać tej Convertklasy (co oznacza: nie używam jej), ponieważ uważam ją za bardzo zagmatwaną: kod daje zbyt mało wskazówek na temat tego, co dokładnie się tutaj dzieje, ponieważ Convertpozwala na wiele semantycznie bardzo różnych konwersji z tym samym kodem . Utrudnia to programiście kontrolowanie tego, co dokładnie się dzieje.

Dlatego radzę nigdy nie używać tej klasy. Nie jest to również konieczne (z wyjątkiem formatowania binarnego liczby, ponieważ normalna ToStringmetoda klas liczbowych nie oferuje odpowiedniej metody do tego).

Konrad Rudolph
źródło
7

Jeśli nie masz 100% pewności co do swoich danych wejściowych, co rzadko się zdarza, powinieneś użyć Double.TryParse.

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.

Szybkość analizy staje się drugorzędna, gdy zgłosisz wyjątek, ponieważ nie jest dużo wolniejszy niż wyjątek.

Pościg
źródło
4

Dużo nienawiści do klasy Convert tutaj ... Żeby trochę zrównoważyć, jest jedna zaleta Convert - jeśli dostaniesz przedmiot,

Convert.ToDouble(o);

może po prostu łatwo zwrócić wartość, jeśli o jest już Double (lub int lub cokolwiek łatwo rzutowalnego).

Używanie Double.Parse lub Double.TryParse jest świetne, jeśli masz już to w ciągu, ale

Double.Parse(o.ToString());

musi najpierw sprawić, że łańcuch zostanie przeanalizowany i w zależności od danych wejściowych może to być droższe.

user1664043
źródło
1
+1: widzę twój punkt widzenia, parsowanie pudełkowych liczb / ciągów z liczbą wewnątrz System.Object jest całkiem proste, zamiast tego masowe sprawdzanie typów. Ale w zasadzie zgadzam się z innymi odpowiedziami: Convert.ToSomething()jest o wiele bardziej kosztowny niż Parse / TryParse, szczególnie w kontekście iteracji
T-moty
Dla mnie Convert.ToDouble (o) ma kilka łatwych wyjść, jeśli to, co jest w pudełku, jest już liczbą, ale prawdziwym zabójcą dla Convert jest to, że nie ma .TryToDouble (o, out d); Biorąc pod uwagę, jak drogie są wyjątki (i jak pewny jesteś - lub nie - danych wejściowych), jest to duży dodatkowy koszt Convert.
user1664043
2

Double.TryParse IMO.

Łatwiej Ci sobie poradzić, będziesz dokładnie wiedzieć, gdzie wystąpił błąd.

Następnie możesz sobie z nim poradzić, jak uważasz za stosowne, jeśli zwróci fałsz (tj. Nie może przekonwertować).

Damien
źródło
2

Zawsze wolałem używać tych TryParse()metod, ponieważ odwdzięczą się one sukcesem lub niepowodzeniem konwersji bez martwienia się o wyjątki.

TheTXI
źródło
2

To interesujące stare pytanie. Dodam odpowiedź, ponieważ w pierwotnym pytaniu nikt nie zauważył kilku rzeczy.

Co jest szybsze: Convert.ToDouble czy Double.TryParse? Co jest bezpieczniejsze: Convert.ToDouble czy Double.TryParse?

Odpowiem szczegółowo na oba te pytania (zaktualizuję odpowiedź później), ale najpierw:

Ze względów bezpieczeństwa rzecz, której każdy programista przegapił w tym pytaniu, to wiersz (moje wyróżnienie):

Dodaje tylko dane, które są liczbami z kilkoma cyframi (1000 1000,2 1000,34 - przecinek jest separatorem w rosyjskich standardach ).

Następnie poniższy przykład kodu:

Convert.ToDouble(regionData, CultureInfo.CurrentCulture);

Co ciekawe, jeśli arkusze kalkulacyjne są w rosyjskim formacie liczbowym, ale program Excel nie wpisał poprawnie pól komórek, jaka jest prawidłowa interpretacja wartości pochodzących z programu Excel?

Oto kolejna interesująca rzecz dotycząca tych dwóch przykładów, dotycząca szybkości:

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

To prawdopodobnie wygeneruje MSIL, który wygląda tak:

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

W tym sensie prawdopodobnie możemy porównać całkowitą liczbę instrukcji MSIL wykonanych przez każdy program - więcej o tym później, gdy zaktualizuję ten post.

Uważam, że kod powinien być poprawny, przejrzysty i szybki ... W takiej kolejności!

John Zabroski
źródło
1

Osobiście uważam, że TryParsemetoda jest łatwiejsza do odczytania, a która z nich faktycznie zechcesz użyć, zależy od twojego przypadku użycia: jeśli błędy mogą być obsługiwane lokalnie, spodziewasz się błędów i wartości bool odTryParse jest dobra, w przeciwnym razie możesz po prostu pozwolić wyjątki latają.

Spodziewałbym się, że TryParserównież będzie szybszy, ponieważ pozwala uniknąć narzutu związanego z obsługą wyjątków. Ale użyj narzędzia testowego, takiego jak MiniBench Jona Skeeta, aby porównać różne możliwości.

David Schmitt
źródło
Opinia na temat tej odpowiedzi: Jeśli pytanie dotyczy tego, co jest szybsze i bezpieczniejsze, odpowiedź nie powinna zaczynać się od: „Osobiście” i zawierać zgadywanie typu: „Spodziewałbym się ...”. To tylko osobiste przemyślenia i komentarz, a nie dobra odpowiedź.
PandaWood