Różnica między rzutowaniem a użyciem metody Convert.To ()

89

Mam funkcję, która rzuca a doublena stringwartości.

string variable = "5.00"; 

double varDouble = (double)variable;

Wprowadzono zmianę kodu i projekt jest kompilowany z błędem: System.InvalidCastException: Specified cast is not valid.

Jednak po wykonaniu następujących czynności ...

string variable = "5.00"; 

double varDouble = Convert.ToDouble(variable);

... projekt buduje się bez żadnych błędów.

Jaka jest różnica między rzutowaniem a zastosowaniem Convert.To()metody? Dlaczego rzucanie rzuca, Exceptiona używanie Convert.To()nie?


źródło
6
Jeśli chodzi o pytanie , do którego się odwołuje , OP pyta, kiedy użyć obsady lub konwersji, a zaakceptowana odpowiedź brzmi: „To naprawdę kwestia wyboru, niezależnie od tego, z której strony korzystasz”. Pytam o różnicę między obsadą a konwerterem. Moim zdaniem poniższe odpowiedzi (brawa SO!) Dostarczają więcej szczegółów na temat różnic w porównaniu z „korzystaniem z tego lub tamtego z wyboru”… i ten szczegół mógłby zasadniczo posłużyć do dokonania bardziej świadomego wyboru.
@ edmastermind29 nie ma dużej różnicy między „jaka jest różnica między x i y” a „kiedy używać x i y” w kontekście programowania. Obie wzajemnie odpowiadają sobie.
nawfal
2
Prawie 3 lata później nie wydaje się, aby w tym przypadku jedna z nich odpowiadała sobie nawzajem. P: „Jaka jest różnica między X i Y?” Odp .: „To naprawdę kwestia wyboru, niezależnie od tego, z której strony korzystasz”. Niezbyt przydatne.
Wydaje się, że nikt nie ma bezpośredniej odpowiedzi na pytanie, która wydajność jest najlepsza, jest również częścią pytania. Z mojego doświadczenia wynika, że ​​Cast jest lepszy, szczególnie w uzyskiwaniu takich wartości kolumn .. (int) datatable.Rows [0] [0], if wiemy, że jest to 100% int
Sundara Prabu

Odpowiedzi:

126

Nawet jeśli można je zobaczyć w jakiś sposób za równoważne są zupełnie inne w celu. Najpierw spróbujmy zdefiniować, czym jest obsada:

Rzutowanie to czynność polegająca na zmianie jednostki jednego typu danych na inny.

Jest to trochę ogólne i jest w pewnym sensie równoważne z konwersją, ponieważ rzutowanie często ma taką samą składnię konwersji, więc pytanie powinno brzmieć, kiedy rzutowanie (niejawne lub jawne) jest dozwolone przez język i kiedy trzeba użyć ( więcej) wyraźna konwersja?

Pozwólcie, że najpierw narysuję między nimi prostą linię. Formalnie (nawet jeśli jest to równoważne dla składni języka) rzutowanie zmieni typ, podczas gdy konwersja zmieni / może zmienić wartość (ostatecznie razem z typem). Obsada jest również odwracalna, podczas gdy konwersja może nie być.

Ten temat jest dość obszerny, więc spróbujmy go nieco zawęzić, wykluczając z gry niestandardowych operatorów rzutów.

Niejawne rzuty

W C # rzutowanie jest niejawne, gdy nie stracisz żadnych informacji (pamiętaj, że to sprawdzenie jest wykonywane z typami, a nie z ich rzeczywistymi wartościami ).

Typy prymitywne

Na przykład:

int tinyInteger = 10;
long bigInteger = tinyInteger;

float tinyReal = 10.0f;
double bigReal = tinyReal;

Rzuty te są niejawne, ponieważ podczas konwersji nie stracisz żadnych informacji (po prostu poszerzysz typ). I odwrotnie, niejawne rzutowanie nie jest dozwolone, ponieważ niezależnie od ich rzeczywistych wartości (ponieważ można je sprawdzić tylko w czasie wykonywania), podczas konwersji możesz utracić niektóre informacje. Na przykład ten kod nie zostanie skompilowany, ponieważ a doublemoże zawierać (i faktycznie ma) wartość, której nie można przedstawić za pomocą float:

// won't compile!
double bigReal = Double.MaxValue;
float tinyReal = bigReal;

Obiekty

W przypadku obiektu (wskaźnika do) rzutowanie jest zawsze niejawne, gdy kompilator może mieć pewność, że typ źródłowy jest klasą pochodną (lub implementuje) typ klasy docelowej, na przykład:

string text = "123";
IFormattable formattable = text;

NotSupportedException derivedException = new NotSupportedException();
Exception baseException = derivedException;

W tym przypadku kompilator wie, że stringimplementuje IFormattablei to NotSupportedExceptionjest (pochodzi z), Exceptionwięc rzutowanie jest niejawne. Żadne informacje nie są tracone, ponieważ obiekty nie zmieniają swoich typów (jest inaczej w przypadku structs i typów prymitywnych, ponieważ za pomocą rzutowania tworzysz nowy obiekt innego typu ), zmienia się twój widok na nie.

Jawne rzuty

Rzutowanie jest jawne, gdy konwersja nie jest wykonywana niejawnie przez kompilator, a następnie należy użyć operatora rzutowania. Zwykle oznacza to, że:

  • Możesz utracić informacje lub dane, więc musisz być tego świadomy.
  • Konwersja może się nie powieść (ponieważ nie możesz przekonwertować jednego typu na inny), więc ponownie musisz być świadomy tego, co robisz.

Typy prymitywne

Jawne rzutowanie jest wymagane w przypadku typów pierwotnych, gdy podczas konwersji możesz utracić część danych, na przykład:

double precise = Math.Cos(Math.PI * 1.23456) / Math.Sin(1.23456);
float coarse = (float)precise;

float epsilon = (float)Double.Epsilon;

W obu przykładach, nawet jeśli wartości mieszczą się w floatzakresie, utracisz informacje (w tym przypadku precyzję), więc konwersja musi być jawna. Teraz spróbuj tego:

float max = (float)Double.MaxValue;

Ta konwersja nie powiedzie się, więc ponownie musi być jawna, abyś był tego świadomy i możesz przeprowadzić kontrolę (w przykładzie wartość jest stała, ale może pochodzić z niektórych obliczeń w czasie wykonywania lub operacji we / wy). Wróćmy do przykładu:

// won't compile!
string text = "123";
double value = (double)text;

To się nie skompiluje, ponieważ kompilator nie może przekonwertować tekstu na liczby. Tekst może zawierać dowolne znaki, a nie tylko liczby, a to za dużo w C #, nawet w przypadku jawnego rzutowania (ale może być dozwolone w innym języku).

Obiekty

Konwersje ze wskaźników (na obiekty) mogą się nie powieść, jeśli typy są niepowiązane, na przykład ten kod nie zostanie skompilowany (ponieważ kompilator wie, że nie ma możliwej konwersji):

// won't compile!    
string text = (string)AppDomain.Current;
Exception exception = (Exception)"abc";

Ten kod zostanie skompilowany, ale może się nie powieść w czasie wykonywania (zależy to od efektywnego typu rzutowanych obiektów) z InvalidCastException:

object obj = GetNextObjectFromInput();
string text = (string)obj;

obj = GetNextObjectFromInput();
Exception exception = (Exception)obj;

Konwersje

W końcu, jeśli rzutowania są konwersjami, to po co nam takie klasy Convert? Ignorowanie subtelnych różnic, które pochodzą z Convertimplementacji i IConvertibleimplementacji, ponieważ w C # z rzutowaniem mówisz do kompilatora:

zaufaj mi, ten typ jest tego typu, nawet jeśli nie możesz tego teraz wiedzieć, pozwól mi to zrobić, a zobaczysz.

-lub-

nie martw się, nie obchodzi mnie, czy coś zostanie utracone podczas tej konwersji.

Do wszystkiego innego potrzebna jest bardziej wyraźna operacja (pomyśl o implikacjach łatwych rzutów , dlatego C ++ wprowadził dla nich długą, pełną i jawną składnię). Może to wiązać się ze złożoną operacją (do konwersji string-> doublebędzie potrzebna analiza). Na przykład konwersja na format stringjest zawsze możliwa ( ToString()metodą), ale może oznaczać coś innego niż to, czego oczekujesz, więc musi być bardziej wyraźna niż rzut ( więcej piszesz, więcej myślisz o tym, co robisz ).

Ta konwersja może być wykonana wewnątrz obiektu (przy użyciu znanych instrukcji IL), przy użyciu niestandardowych operatorów konwersji (zdefiniowanych w klasie do rzutowania) lub bardziej złożonych mechanizmów ( TypeConverterna przykład metod lub metod klas). Nie jesteś świadomy tego, co się stanie, ale zdajesz sobie sprawę, że może się to nie udać (dlatego IMO, gdy możliwa jest bardziej kontrolowana konwersja, powinieneś jej użyć). W twoim przypadku konwersja po prostu przeanalizuje, stringtworząc double:

double value = Double.Parse(aStringVariable);

Oczywiście może się to nie powieść, więc jeśli to zrobisz, zawsze powinieneś złapać wyjątek, który może rzucić ( FormatException). To jest poza tematem, ale kiedy TryParsejest dostępne, powinieneś go użyć (ponieważ semantycznie mówisz, że może to nie być liczba, a nawet szybciej ... zawieść).

Konwersje w .NET mogą pochodzić z wielu miejsc, TypeConverterniejawnych / jawnych rzutów ze zdefiniowanymi przez użytkownika operatorami konwersji, implementacji IConvertiblei analizowania metod (czy o czymś zapomniałem?). Zajrzyj na MSDN, aby uzyskać więcej informacji na ich temat.

Na zakończenie tej długiej odpowiedzi wystarczy kilka słów o operatorach konwersji zdefiniowanych przez użytkownika. To po prostu cukier, aby pozwolić programiście na użycie rzutowania do konwersji jednego typu na inny. Jest to metoda wewnątrz klasy (tej, która zostanie rzutowana), która mówi "hej, jeśli on / ona chce przekonwertować ten typ na ten typ, mogę to zrobić". Na przykład:

float? maybe = 10; // Equals to Nullable<float> maybe = 10;
float sure1 = (float)maybe; // With cast
float sure2 = maybe.Value; // Without cast

W tym przypadku jest to wyraźne, ponieważ może się nie powieść, ale jest to dozwolone do implementacji (nawet jeśli istnieją wytyczne na ten temat). Wyobraź sobie, że piszesz niestandardową klasę ciągów w następujący sposób:

EasyString text = "123"; // Implicit from string
double value = (string)text; // Explicit to double

W swojej implementacji możesz zdecydować się na „ułatwienie życia programistom” i ujawnienie tej konwersji poprzez rzutowanie (pamiętaj, że to tylko skrót do pisania mniej). Niektóre języki mogą nawet na to pozwolić:

double value = "123";

Umożliwienie niejawnej konwersji do dowolnego typu (sprawdzenie zostanie wykonane w czasie wykonywania). Przy odpowiednich opcjach można to zrobić na przykład w VB.NET. To po prostu inna filozofia.

Co mogę z nimi zrobić?

Więc ostatnie pytanie brzmi: kiedy powinieneś użyć jednego lub drugiego. Zobaczmy, kiedy możesz użyć wyraźnej obsady:

  • Konwersje między typami podstawowymi.
  • Konwersje z objectdo dowolnego innego typu (może to obejmować również rozpakowywanie).
  • Konwersje z klasy pochodnej do klasy bazowej (lub do zaimplementowanego interfejsu).
  • Konwersje z jednego typu na inny za pomocą niestandardowych operatorów konwersji.

Można wykonać tylko pierwszą konwersję, Convertwięc w przypadku innych nie masz wyboru i musisz użyć jawnej obsady.

Zobaczmy teraz, kiedy możesz użyć Convert:

  • Konwersje z dowolnego typu podstawowego na inny typ podstawowy (z pewnymi ograniczeniami, zobacz MSDN ).
  • Konwersje z dowolnego typu, który jest implementowany, IConvertiblena dowolny inny (obsługiwany) typ.
  • Konwersje z / na bytetablicę na / z ciągu.

Wnioski

IMO Convertpowinno być używane za każdym razem, gdy wiesz, że konwersja może się nie powieść (ze względu na format, zakres lub może być nieobsługiwany), nawet jeśli tę samą konwersję można wykonać za pomocą rzutowania (chyba że jest dostępne coś innego). Wyjaśnia, kto będzie czytał Twój kod, jaki jest Twój zamiar i że może się nie powieść (uproszczenie debugowania).

Do wszystkiego innego potrzebujesz odlewu, nie ma wyboru, ale jeśli dostępna jest inna lepsza metoda, sugeruję jej użycie. W twoim przykładzie konwersja z stringdo doublejest czymś, co (zwłaszcza jeśli tekst pochodzi od użytkownika) bardzo często kończy się niepowodzeniem, więc powinieneś uczynić ją tak wyraźną, jak to tylko możliwe (a ponadto masz nad nią większą kontrolę), na przykład używając TryParsemetody.

Edycja: jaka jest różnica między nimi?

Zgodnie ze zaktualizowanym pytaniem i zachowaniem tego, co napisałem wcześniej (o tym, kiedy można użyć rzutowania w porównaniu do tego, kiedy można / trzeba go użyć Convert), ostatni punkt do wyjaśnienia to czy są między nimi różnice (ponadto Convertużywa IConvertiblei IFormattableinterfejsów, aby mógł wykonywać operacje niedozwolone przy odlewach).

Krótka odpowiedź brzmi: tak, zachowują się inaczej . Postrzegam tę Convertklasę jako klasę metod pomocniczych, więc często zapewnia pewne korzyści lub nieco inne zachowanie. Na przykład:

double real = 1.6;
int castedInteger = (int)real; // 1
int convertedInteger = Convert.ToInt32(real); // 2

Całkiem inny, prawda? Rzutowanie jest obcięte (wszyscy tego oczekujemy), ale Convertwykonuje zaokrąglenie do najbliższej liczby całkowitej (a tego można się nie spodziewać, jeśli nie jesteś tego świadomy). Każda metoda konwersji wprowadza różnice, więc nie można zastosować ogólnej reguły i należy je rozpatrywać w każdym przypadku ... 19 typów podstawowych do konwersji na każdy inny typ ... lista może być dość długa, o wiele lepiej jest sprawdzić wielkość liter w MSDN walizka!

Adriano Repetti
źródło
Zmieniłem zadać pytanie, Difference between casting and using the Convert.To() method. W przeciwnym razie bardzo wyczerpująca odpowiedź. (Mam nadzieję, że moje pytanie zostanie ponownie otwarte ...)
@ edmastermind29 Trochę zredagowałem pytanie, temat jest za długi nawet na długą odpowiedź (ponad 300 możliwych konwersji do wyświetlenia). Convert dodaje korzyści (lub po prostu nieoczekiwane zachowania?) Nie tylko w porównaniu z rzutowaniem, ale także w porównaniu z "zwykłymi" interfejsami IConvertible i IFormattable.
Adriano Repetti
Nie podoba mi się koncepcja zapożyczona z C, że doublewartości, które nie reprezentują liczb całkowitych, powinny być „zamienialne” na int. Rzutowanie wydawałoby się odpowiednim paradygmatem w przypadkach, gdy np. Ktoś pobiera Int32wartości z a, double[]które przechowuje mieszankę liczb rzeczywistych i Int32wartości, które zostały przekonwertowane na double[próba konwersji wartości, której nie można dokładnie przedstawić w, int32oznaczałaby nieoczekiwany stan i powinien wywołać wyjątek], ale myślę, że kiedy chce się konwersji stratnej, należy dokładnie określić żądaną formę.
supercat
1
Inna różnica dotyczy typów obiektowych do typów pierwotnych. np.object o = 123; var l = Convert.ToInt64(o); var i = (long) (int) o; var f = (long) o // InvalidCastException
yue shi
1
@ rory.ap to ważna kwestia. Nie, formalnie to nie jest obsada ( float-> int), ale przymus . Może to być na przykład DerivedClass-> BaseClass. Jest to mylące, ponieważ w C # używamy tego samego słowa (i operatora) dla obu, ale w rzeczywistości są to różne rzeczy. Formalna definicja rozróżnienia między nimi jest nieco bardziej skomplikowana niż to, co napisałem.
Adriano Repetti
12

Rzutowanie jest sposobem na powiedzenie kompilatorowi: „Wiem, że myślisz, że ta zmienna to Bar, ale ja wiem więcej niż ty; obiekt to w rzeczywistości Foo, więc potraktuję go tak, jakby to było Foo z teraz. ” Następnie, w czasie wykonywania, jeśli rzeczywisty obiekt okazał się naprawdę Foo, wtedy twój kod działa, jeśli okaże się, że obiekt w ogóle nie był Foo, wtedy pojawi się wyjątek. (W szczególności an System.InvalidCastException.)

Z drugiej strony konwersja jest sposobem na powiedzenie: „Jeśli dasz mi obiekt typu Bar, mogę stworzyć zupełnie nowy obiekt Foo, który reprezentuje to, co jest w tym obiekcie Bar. Nie zmienię oryginalnego obiektu, wygrał” Jeśli traktuje oryginalny obiekt inaczej, utworzy coś nowego, opartego tylko na innej wartości . Jeśli chodzi o sposób, w jaki to zrobi, może to być wszystko. W takim przypadku Convert.ToDoublewywołanieDouble.Parsektóry ma różnego rodzaju złożoną logikę do określania, jakie typy łańcuchów reprezentują wartości liczbowe. Mógłbyś napisać własną metodę konwersji, która odwzorowuje łańcuchy na dublety w inny sposób (być może w celu obsługi jakiejś zupełnie innej konwencji wyświetlania liczb, takich jak cyfry rzymskie lub cokolwiek innego). Konwersja może zrobić wszystko, ale chodzi o to, że tak naprawdę nie prosisz kompilatora, aby zrobił cokolwiek za Ciebie; to ty piszesz kod, aby określić, jak utworzyć nowy obiekt, ponieważ kompilator bez Twojej pomocy nie może wiedzieć, jak odwzorować (na przykład) a stringna double.

Więc kiedy się nawracasz, a kiedy rzucasz? W obu przypadkach mamy jakąś zmienną typu, powiedzmy A, i chcemy mieć zmienną typu B. Jeśli nasz obiekt A naprawdę, w rzeczywistości, pod maską, jest B, to rzucamy. Jeśli tak naprawdę nie jest to B, musimy go przekonwertować i zdefiniować, w jaki sposób program ma uzyskać B z A.

Servy
źródło
W jednym z postów SO Eric Lippert wspomniał, że nie ma czegoś takiego, jak niejawne rzutowanie i jest to niejawna konwersja . Używam zamiennie odlewu i konwersji. co jest złego w mówieniu „ukryta obsada”? Czy nie można powiedzieć, że jeśli konwersja jest niejawna i nie wymaga żadnego rzutowania, jest to „niejawne rzutowanie”?
rahulaga_dev
1
@RahulAgarwal Rzutowanie to operacja, w której musisz wyraźnie wskazać, że dany typ jest (lub może zostać przekształcony) w prawidłową instancję innego typu. Gdy istnieje niejawna konwersja, rzutowanie nie jest potrzebne do traktowania typu jako innego typu. Więc powiedzenie „niejawne rzutowanie” nie ma sensu (z wyjątkiem potencjalnie kilku sytuacji, o których wspomniał Eric, gdzie operator rzutowania jest dodawany bez wpisywania go przez programistę, na przykład przy użyciu a foreach). Poza tymi wyjątkami rzuty są z definicji jawne.
Servy
5

Od MSDN:

Jawne konwersje (rzutowanie): Jawne konwersje wymagają operatora rzutowania. Przesyłanie jest wymagane, gdy informacje mogą zostać utracone podczas konwersji lub gdy konwersja może się nie powieść z innych powodów. Typowe przykłady obejmują konwersję liczbową na typ, który ma mniejszą precyzję lub mniejszy zakres, oraz konwersję wystąpienia klasy bazowej na klasę pochodną.

Rozważmy następujący przykład:

double a = 2548.3;
int b;
b = (int)a; //2548 --> information (.3) lost in the conversion

I również:

Rzutowanie to sposób jawnego poinformowania kompilatora, że ​​zamierzasz dokonać konwersji i że zdajesz sobie sprawę, że może nastąpić utrata danych.

Możesz użyć System.Convertclass, gdy chcesz konwertować między niekompatybilnymi typami. Główną różnicą między odlewania i nawróconego jest kompilacji i czasu wykonywania . Wyjątki konwersji typów pojawiają się w czasie wykonywania , tj. Rzutowanie typu, które nie powiedzie się w czasie wykonywania, spowoduje zgłoszenie InvalidCastException.


Wniosek: podczas rzutowania mówisz kompilatorowi, który ajest naprawdę typem, ba jeśli tak, projekt kompiluje się bez żadnych błędów, takich jak ten przykład:

double s = 2;
int a = (int) s;

Ale w konwersji mówisz do kompilatora istnieje sposób, aby utworzyć nowy obiekt z atypu b, zrób to i projekt buduje bez żadnych błędów, ale jak powiedziałem , jeśli typ nie oddanych w czasie wykonywania, spowoduje to InvalidCastException, aby być rzuconym .

Na przykład poniższy kod nigdy nie jest kompilowany, ponieważ kompilator wykrył, że nie może rzutować wyrażenia typu DateTimena typ int:

DateTime s = DateTime.Now;
int a = (int)(s);

Ale ten został pomyślnie skompilowany:

DateTime s = DateTime.Now;
int a = Convert.ToInt32(s);

Ale w czasie wykonywania otrzymasz komunikat InvalidCastException:

Nieprawidłowe rzutowanie z „DateTime” na „Int32”.

Salah Akbari
źródło
4

W Convert.Doublerzeczywistości metoda wywołuje ją tylko wewnętrznie Double.Parse(string).

Ani Stringtyp, ani typ nie Doubledefiniują jawnej / niejawnej konwersji między dwoma typami, więc rzutowanie zawsze kończy się niepowodzeniem.

Double.ParseMetoda będzie wyglądać w każdej postaci w stringi budować wartość numeryczną opartą na wartościach bohaterów w string. Jeśli którykolwiek ze znaków jest nieprawidłowy, Parsemetoda kończy się niepowodzeniem (powodując również Convert.Doubleniepowodzenie metody).

Dan
źródło
1
I czym się to różni od wyraźnej obsady?
3
Jawne rzutowanie nie sprawdza, jaki jest typ danych, po prostu patrzy na bajty. Przykładem może być rzutowanie znaku char x = '1' na liczbę całkowitą, liczba ta będzie równa 49, ponieważ znak '1' jest # 49 w tabeli ascii
user1751547
@ user1751547 Czy więc użytkownik Convert.ToDouble()spojrzałby poza bajty i rozważył dane?
@ user1751547 Myślę, że taka intuicja jest wymagana, aby właściwie odpowiedzieć na to pytanie. Samo powiedzenie „nie jest zdefiniowane” jest trochę dyskusyjne.
Ant P
@ edmastermind29 Tak, sprawdziłby typ danych wejściowych i gdyby był to łańcuch, przeszedłby przez każdy znak, wiedząc, że jeśli wartość ascii znaku wynosi 49, to jest to znak '1' i poprawnie go przekonwertuje
user1751547
3

W Twoim przykładzie próbujesz rzutować ciąg na podwójny (typ niecałkowity).

Aby to zadziałało, wymagana jest wyraźna konwersja.

I muszę zaznaczyć, że mogłeś użyć Convert.ToDoublezamiast, Convert.ToInt64ponieważ możesz stracić ułamkowe części podwójnej wartości podczas konwersji na int.

jeśli zmienna ma wartość „5,25” varDouble wyniósłby 5,00 (strata 0,25 z powodu konwersji na Int64)

Aby odpowiedzieć na Twoje pytanie dotyczące przesyłania i konwersji.

Twoja obsada (jawna obsada) nie spełnia wymagań dla jawnej obsady. wartość, którą próbujesz rzutować za pomocą operatora rzutowania, jest nieprawidłowa (tj. niecałkowita).

Odwiedź tę stronę MSDN, aby zapoznać się z zasadami przesyłania / konwersji

scartag
źródło
@ edmastermind29 Zaktualizowałem moją odpowiedź. mam nadzieję, że odpowiada na twoje pytanie.
scartag
Jakie są wymagania dotyczące jawnej obsady ... w odniesieniu do mojego pytania? Czy chodzi o wartość „niecałkowitą”?
@ edmastermind29 Yes. jeśli wartość, którą próbujesz rzutować na typ liczbowy, nie jest liczbą, rzutowanie jest nieprawidłowe. Wymagana jest konwersja.
scartag
3

Rzutowanie nie obejmuje żadnej konwersji, tj. Wewnętrzna reprezentacja wartości nie ulega zmianie. Przykład:

object o = "Hello"; // o is typed as object and contains a string.
string s = (string)o; // This works only if o really contains a string or null.

Możesz przekonwertować a doublena stringtaki

double d = 5;
string s = d.ToString(); // -> "5"

// Or by specifying a format
string formatted = d.ToString("N2"); // -> "5.00"

Możesz przekonwertować a stringna a doublena kilka sposobów (tutaj tylko dwa z nich):

string s = "5";
double d = Double.Parse(s); // Throws an exception if s does not contain a valid number

Albo w bezpieczny sposób

string s = "5";
double d;
if (Double.TryParse(s, out d)) {
    Console.WriteLine("OK. Result = {0}", d);
} else {
    Console.WriteLine("oops!");
}
Olivier Jacot-Descombes
źródło
Convert.ToDouble()połączenia wewnętrzneDouble.Parse() . Czy korzystanie z opcji „ Convert.ToDouble()over” Double.Parse()lub „ over” jest korzystne i dlaczego?
Convert.ToDoublema wiele przeciążeń, które akceptują różne typy danych wejściowych. Akceptowanie przeciążenia stringzwraca, 0.0jeśli nullprzekazano ciąg. Poza tym nie widzę żadnej korzyści w używaniu go.
Olivier Jacot-Descombes
Więc albo albo ... albo Double.Parse()ma coś do zaoferowania, co powinienem rozważyć?
Double.Parse()jest bardziej bezpośredni niż Convert.ToDouble(). Jeśli jesteś pewien, że Twój ciąg będzie zawierał prawidłową liczbę, możesz go bezpiecznie użyć, w przeciwnym razie radzę użyć Double.TryParse.
Olivier Jacot-Descombes
1
string variable = "5.00";     
double varDouble = (double)variable;

Powyższa konwersja jest po prostu niedozwolona przez język. Oto lista wyraźnych rzutów dla typów liczbowych: http://msdn.microsoft.com/en-us/library/yht2cx7b.aspx Jak widać, nawet nie każdy typ liczbowy można przekonwertować na inny typ liczbowy

Więcej informacji o przesyłaniu tutaj

Czym różni się to od Convert.ToDouble ()?

Podczas rzutowania typu struktura danych nie ulega zmianie. Cóż, w przypadku konwersji wartości liczbowych można stracić kilka bitów lub uzyskać dodatkowe 0 bitów. Ale nadal pracujesz z liczbą.Po prostu zmieniasz ilość pamięci zajmowanej przez tę liczbę. Jest to wystarczająco bezpieczne, aby kompilator zrobił wszystko, co konieczne.

Ale kiedy próbujesz rzucić łańcuch na liczbę, nie możesz tego zrobić, ponieważ nie wystarczy zmienić ilości pamięci zajmowanej przez zmienną. Na przykład, 5.00jako łańcuch jest sekwencją „liczb”: 53 (5) 46 (.) 48 (0) 48 (0) - to jest dla ASCII, ale łańcuch będzie zawierał coś podobnego. Jeśli kompilator po prostu pobierze pierwsze N ​​(4 dla podwójnego? Nie jestem pewien) z łańcucha - ten fragment będzie zawierał zupełnie inną podwójną liczbę. W tym samym czasie Convert.ToDouble () uruchamia specjalny algorytm, który pobierze każdy symbol łańcucha, obliczy cyfrę, którą reprezentuje i utworzy podwójną liczbę, jeśli łańcuch reprezentuje liczbę. Języki takie jak PHP, z grubsza mówiąc, będą wywoływać dla Ciebie Convert.ToDouble w tle. Ale C #, podobnie jak język statyczny, nie zrobi tego za Ciebie. Dzięki temu możesz mieć pewność, że każda operacja jest bezpieczna dla typu i nie otrzymasz czegoś nieoczekiwanego, wykonując coś takiego jak:

double d = (double)"zzzz"
Viktor S.
źródło
@ edmastermind29 zobacz moją zaktualizowaną odpowiedź. Próbowałem to wyjaśnić. Wyjaśnienie jest dalekie od doskonałości, ale przypuśćmy, że wyjaśnia różnicę.
Viktor S.
1

Rzutowanie ciągu na taki podwójny jest niedozwolone C # i dlatego otrzymujesz wyjątek, musisz przekonwertować ciąg ( dokument MSDN, który pokazuje dopuszczalne ścieżki konwersji). Dzieje się tak po prostu dlatego, że łańcuch niekoniecznie będzie zawierał dane liczbowe, ale różne typy liczbowe będą (z wyjątkiem wartości null). A Converturuchomi metodę, która sprawdzi łańcuch, aby zobaczyć, czy można go przekształcić w wartość liczbową. Jeśli tak, zwróci tę wartość. Jeśli nie, zgłosi wyjątek.

Aby go przekonwertować, masz kilka opcji. Użyłeś Convertmetodę w swoim pytaniu, nie ma Parseco jest w dużej mierze podobny do Convert, ale należy również spojrzeć na TryParse który pozwoli Ci zrobić:

string variable = "5.00"; 

double varDouble;

if (Double.TryParse(variable, out varDouble)) {
    //Code that runs if the conversion succeeded.
} else {
    //Code that runs if the conversion failed.
}

Pozwala to uniknąć możliwego wyjątku, jeśli spróbujesz Convertlub Parseciągu nieliczbowego.

Zapalony
źródło
Czy skorzystanie z opcji TryParseover, Convertponieważ TryParsesprawdza, czy konwersja się powiodła?
@ edmastermind29 Myślę, że tak. Convert zgłosi wyjątek, jeśli konwersja nie powiedzie się. TryParse zwróci wartość logiczną True, jeśli konwersja się powiedzie, a False, jeśli się nie powiedzie.
Keen
1

double varDouble = (double)variablezakłada, że variablejest to już podwójne. Jeśli variablenie jest podwójnym (jest to ciąg), to się nie powiedzie. double varDouble = Convert.ToDouble(variable)lubi, jak mówi - konwertuje.Jeśli może przeanalizować lub w inny sposób wyodrębnić podwójną wartość, variableto zrobi.

Po drugie używam Double.Parselub Double.TryParsedlatego, że wyraźniej wskazuje, co ma się dziać. Zaczynasz od łańcucha i oczekujesz, że będzie można go zamienić na podwójny. Jeśli masz jakiekolwiek wątpliwości, użyj TryParse.

Jeśli variablejest argumentem metody, zmień typ na double. Spraw, aby dzwoniący był odpowiedzialny za podanie prawidłowego typu. W ten sposób kompilator wykona pracę za Ciebie.

Scott Hannen
źródło
-1

Najważniejsza różnica polega na tym, że jeśli używane jest rzutowanie typów i konwersja nie powiedzie się (powiedzmy, że konwertujemy bardzo dużą wartość zmiennoprzecinkową na wartość int), nie zostanie zgłoszony żaden wyjątek i zostanie wyświetlona minimalna wartość, jaką można przechowywać w wartości int. Ale w przypadku korzystania z Convert , wyjątek zostanie zgłoszony dla takich scenariuszy.

Tom Hardy
źródło