Anche se si può vedere in qualche modo equivalenti sono completamente diverse in proposito. Proviamo prima a definire cos'è un cast:
Il casting è l'azione di cambiare un'entità di un tipo di dati in un altro.
È un po 'generico ed è in qualche modo equivalente a una conversione perché un cast spesso ha la stessa sintassi di una conversione, quindi la domanda dovrebbe essere quando un cast (implicito o esplicito) è consentito dalla lingua e quando devi usare un ( più) conversione esplicita?
Vorrei prima tracciare una linea semplice tra di loro. Formalmente (anche se equivalente per la sintassi del linguaggio) un cast cambierà il tipo mentre una conversione cambierà / potrebbe cambiare il valore (eventualmente insieme al tipo). Anche un cast è reversibile mentre una conversione potrebbe non esserlo.
Questo argomento è piuttosto vasto, quindi proviamo a restringerlo un po 'escludendo gli operatori di cast personalizzati dal gioco.
Cast impliciti
In C # un cast è implicito quando non perderai alcuna informazione (tieni presente che questo controllo viene eseguito con i tipi e non con i loro valori effettivi ).
Tipi primitivi
Per esempio:
int tinyInteger = 10;
long bigInteger = tinyInteger;
float tinyReal = 10.0f;
double bigReal = tinyReal;
Questi cast sono impliciti perché durante la conversione non perderai alcuna informazione (devi solo allargare il tipo). Viceversa il cast implicito non è consentito perché, indipendentemente dai loro valori effettivi (perché possono essere verificati solo in fase di esecuzione), durante la conversione potresti perdere alcune informazioni. Ad esempio, questo codice non verrà compilato perché a doublepuò contenere (e in realtà lo fa) un valore non rappresentabile con a float:
double bigReal = Double.MaxValue;
float tinyReal = bigReal;
Oggetti
Nel caso di un oggetto (un puntatore a) il cast è sempre implicito quando il compilatore può essere certo che il tipo di origine è una classe derivata (o implementa) il tipo della classe di destinazione, ad esempio:
string text = "123";
IFormattable formattable = text;
NotSupportedException derivedException = new NotSupportedException();
Exception baseException = derivedException;
In questo caso il compilatore sa che stringimplementa IFormattablee che NotSupportedExceptionè (deriva da) Exceptionquindi il cast è implicito. Nessuna informazione viene persa perché gli oggetti non cambiano il loro tipo (questo è diverso con se con structi tipi primitivi perché con un cast crei un nuovo oggetto di un altro tipo ), ciò che cambia è la tua visione di essi.
Cast espliciti
Un cast è esplicito quando la conversione non viene eseguita in modo implicito dal compilatore e quindi è necessario utilizzare l'operatore cast. Di solito significa che:
- Potresti perdere informazioni o dati, quindi devi esserne consapevole.
- La conversione potrebbe non riuscire (perché non puoi convertire un tipo in un altro) quindi, ancora una volta, devi essere consapevole di quello che stai facendo.
Tipi primitivi
È richiesto un cast esplicito per i tipi primitivi quando durante la conversione potresti perdere alcuni dati, ad esempio:
double precise = Math.Cos(Math.PI * 1.23456) / Math.Sin(1.23456);
float coarse = (float)precise;
float epsilon = (float)Double.Epsilon;
In entrambi gli esempi, anche se i valori rientrano floatnell'intervallo, si perderanno le informazioni (in questo caso la precisione) quindi la conversione deve essere esplicita. Ora prova questo:
float max = (float)Double.MaxValue;
Questa conversione fallirà quindi, ancora una volta, deve essere esplicita in modo che tu ne sia consapevole e puoi fare un controllo (nell'esempio il valore è costante ma potrebbe provenire da alcuni calcoli di runtime o I / O). Torna al tuo esempio:
string text = "123";
double value = (double)text;
Questo non verrà compilato perché il compilatore non può convertire il testo in numeri. Il testo può contenere qualsiasi carattere, non solo numeri e questo è troppo, in C #, anche per un cast esplicito (ma potrebbe essere consentito in un'altra lingua).
Oggetti
Le conversioni da puntatori (a oggetti) potrebbero non riuscire se i tipi non sono correlati, ad esempio questo codice non verrà compilato (perché il compilatore sa che non è possibile alcuna conversione):
string text = (string)AppDomain.Current;
Exception exception = (Exception)"abc";
Questo codice verrà compilato ma potrebbe non riuscire in fase di esecuzione (dipende dal tipo effettivo di oggetti sottoposti a cast) con un InvalidCastException:
object obj = GetNextObjectFromInput();
string text = (string)obj;
obj = GetNextObjectFromInput();
Exception exception = (Exception)obj;
Conversioni
Quindi, infine, se i cast sono conversioni, perché abbiamo bisogno di classi come Convert? Ignorando le sottili differenze che derivano dall'implementazione Converte dalle IConvertibleimplementazioni in realtà perché in C # con un cast dici al compilatore:
credimi, questo tipo è quel tipo anche se non puoi saperlo ora, fammelo fare e vedrai.
-o-
non preoccuparti, non mi interessa se qualcosa andrà perso in questa conversione.
Per qualsiasi altra cosa è necessaria un'operazione più esplicita (si pensi alle implicazioni dei cast facili , ecco perché il C ++ ha introdotto per loro una sintassi lunga, dettagliata ed esplicita). Ciò può comportare un'operazione complessa (per la conversione string-> doublesarà necessaria un'analisi). Una conversione a string, ad esempio, è sempre possibile (tramite ToString()metodo) ma può significare qualcosa di diverso da quello che ti aspetti quindi deve essere più esplicito di un cast ( più scrivi, più pensi a quello che stai facendo ).
Questa conversione può essere eseguita all'interno dell'oggetto (utilizzando istruzioni IL note per questo), utilizzando operatori di conversione personalizzati (definiti nella classe da eseguire il cast) o meccanismi più complessi ( TypeConverterso metodi di classe, ad esempio). Non sei consapevole di cosa succederà per farlo, ma sei consapevole che potrebbe fallire (ecco perché IMO quando è possibile una conversione più controllata dovresti usarlo). Nel tuo caso la conversione analizzerà semplicemente il stringper produrre un double:
double value = Double.Parse(aStringVariable);
Ovviamente questo potrebbe fallire, quindi se lo fai dovresti sempre catturare l'eccezione che potrebbe lanciare ( FormatException). È fuori tema qui, ma quando a TryParseè disponibile, dovresti usarlo (perché semanticamente dici che potrebbe non essere un numero ed è ancora più veloce ... fallire).
Le conversioni in .NET possono provenire da molti luoghi, TypeConvertercast impliciti / espliciti con operatori di conversione definiti dall'utente, implementazione IConvertiblee metodi di analisi (ho dimenticato qualcosa?). Dai un'occhiata a MSDN per maggiori dettagli su di loro.
Per concludere questa lunga risposta, solo poche parole sugli operatori di conversione definiti dall'utente. È solo zucchero lasciare che il programmatore usi un cast per convertire un tipo in un altro. È un metodo all'interno di una classe (quella che verrà lanciata) che dice "ehi, se lui / lei vuole convertire questo tipo in quel tipo allora posso farlo io". Per esempio:
float? maybe = 10;
float sure1 = (float)maybe;
float sure2 = maybe.Value;
In questo caso è esplicito perché potrebbe fallire ma questo è lasciato all'implementazione (anche se ci sono linee guida in merito). Immagina di scrivere una classe stringa personalizzata come questa:
EasyString text = "123";
double value = (string)text;
Nella tua implementazione potresti decidere di "semplificare la vita del programmatore" e di esporre questa conversione tramite un cast (ricorda che è solo una scorciatoia per scrivere di meno). Alcune lingue possono persino consentire questo:
double value = "123";
Consentire la conversione implicita in qualsiasi tipo (il controllo verrà eseguito in fase di esecuzione). Con le opzioni appropriate questo può essere fatto, ad esempio, in VB.NET. È solo una filosofia diversa.
Cosa posso fare con loro?
Quindi la domanda finale è quando dovresti usare l'uno o l'altro. Vediamo quando puoi usare un cast esplicito:
- Conversioni tra tipi di base.
- Conversioni da
objecta qualsiasi altro tipo (questo può includere anche l'unboxing).
- Conversioni da una classe derivata a una classe base (o a un'interfaccia implementata).
- Conversioni da un tipo a un altro tramite operatori di conversione personalizzati.
Solo la prima conversione può essere eseguita, Convertquindi per gli altri non hai scelta e devi usare un cast esplicito.
Vediamo ora quando puoi usare Convert:
- Conversioni da qualsiasi tipo di base a un altro tipo di base (con alcune limitazioni, vedere MSDN ).
- Conversioni da qualsiasi tipo implementato
IConvertiblea qualsiasi altro tipo (supportato).
- Conversioni da / a un
bytearray a / da una stringa.
Conclusioni
IMO Convertdovrebbe essere utilizzato ogni volta che sai che una conversione potrebbe non riuscire (a causa del formato, dell'intervallo o perché potrebbe non essere supportato), anche se la stessa conversione può essere eseguita con un cast (a meno che non sia disponibile qualcos'altro). Rende chiaro a chi leggerà il tuo codice qual è il tuo intento e che potrebbe fallire (semplificando il debug).
Per tutto il resto è necessario utilizzare un cast, nessuna scelta, ma se è disponibile un altro metodo migliore allora ti consiglio di usarlo. Nel tuo esempio una conversione da stringa doubleè qualcosa che (specialmente se il testo proviene dall'utente) molto spesso fallirà, quindi dovresti renderla il più esplicita possibile (inoltre hai più controllo su di essa), ad esempio utilizzando un TryParsemetodo.
Modifica: qual è la differenza tra loro?
Secondo la domanda aggiornata e mantenendo ciò che ho scritto prima (su quando puoi usare un cast rispetto a quando puoi / devi usare Convert), l'ultimo punto per chiarire è se ci sono differenze tra loro (inoltre Convertusi IConvertiblee IFormattableinterfacce in modo che possa eseguire operazioni non consentito con calchi).
La risposta breve è sì, si comportano diversamente . Vedo la Convertclasse come una classe di metodi di supporto così spesso che fornisce alcuni vantaggi o comportamenti leggermente diversi. Per esempio:
double real = 1.6;
int castedInteger = (int)real;
int convertedInteger = Convert.ToInt32(real);
Piuttosto diverso, vero? Il cast viene troncato (è quello che tutti ci aspettiamo) ma Convertesegue un arrotondamento all'intero più vicino (e questo potrebbe non essere previsto se non ne sei consapevole). Ogni metodo di conversione introduce differenze, quindi una regola generale non può essere applicata e devono essere viste caso per caso ... 19 tipi di base da convertire in ogni altro tipo ... l'elenco può essere piuttosto lungo, molto meglio consultare MSDN caso per caso Astuccio!