Mesmo que você possa vê-los de alguma forma como equivalentes, eles têm propósitos completamente diferentes. Vamos primeiro tentar definir o que é um elenco:
Casting é a ação de transformar uma entidade de um tipo de dados em outro.
É um pouco genérico e de alguma forma equivalente a uma conversão porque um elenco geralmente tem a mesma sintaxe de uma conversão, então a questão deve ser quando um elenco (implícito ou explícito) é permitido pela linguagem e quando você deve usar um ( mais) conversão explícita?
Deixe-me primeiro traçar uma linha simples entre eles. Formalmente (mesmo se equivalente para a sintaxe da linguagem), um elenco irá alterar o tipo, enquanto uma conversão irá / pode alterar o valor (eventualmente junto com o tipo). Além disso, um elenco é reversível, enquanto uma conversão pode não ser.
Este tópico é muito vasto, então vamos tentar restringi-lo um pouco excluindo os operadores de elenco personalizados do jogo.
Casts implícitos
Em C #, uma conversão está implícita quando você não perderá nenhuma informação (observe que essa verificação é realizada com tipos e não com seus valores reais ).
Tipos primitivos
Por exemplo:
int tinyInteger = 10;
long bigInteger = tinyInteger;
float tinyReal = 10.0f;
double bigReal = tinyReal;
Esses casts são implícitos porque durante a conversão você não perderá nenhuma informação (você apenas torna o tipo mais amplo). O cast implícito vice-versa não é permitido porque, independentemente de seus valores reais (porque eles podem ser verificados apenas em tempo de execução), durante a conversão você pode perder algumas informações. Por exemplo, este código não compilará porque a double
pode conter (e realmente contém) um valor não representável com float
:
double bigReal = Double.MaxValue;
float tinyReal = bigReal;
Objetos
No caso de um objeto (um ponteiro para), o elenco está sempre implícito quando o compilador pode ter certeza de que o tipo de origem é uma classe derivada (ou implementa) o tipo da classe de destino, por exemplo:
string text = "123";
IFormattable formattable = text;
NotSupportedException derivedException = new NotSupportedException();
Exception baseException = derivedException;
Nesse caso, o compilador sabe que string
implementa IFormattable
e que NotSupportedException
é (deriva de), Exception
portanto, o elenco está implícito. Nenhuma informação é perdida porque os objetos não mudam seus tipos (isso é diferente com se struct
tipos primitivos porque com um elenco você cria um novo objeto de outro tipo ), o que muda é a sua visão deles.
Casts explícitos
Uma conversão é explícita quando a conversão não é feita implicitamente pelo compilador e você deve usar o operador de conversão. Normalmente significa que:
- Você pode perder informações ou dados, portanto, deve estar ciente disso.
- A conversão pode falhar (porque você não pode converter um tipo em outro), então, novamente, você deve estar ciente do que está fazendo.
Tipos primitivos
Uma conversão explícita é necessária para tipos primitivos quando durante a conversão você pode perder alguns dados, por exemplo:
double precise = Math.Cos(Math.PI * 1.23456) / Math.Sin(1.23456);
float coarse = (float)precise;
float epsilon = (float)Double.Epsilon;
Em ambos os exemplos, mesmo se os valores estiverem dentro do float
intervalo, você perderá informações (neste caso, a precisão), portanto, a conversão deve ser explícita. Agora tente isto:
float max = (float)Double.MaxValue;
Esta conversão irá falhar, então, novamente, ela deve ser explícita para que você esteja ciente disso e possa fazer uma verificação (no exemplo, o valor é constante, mas pode vir de alguns cálculos de tempo de execução ou E / S). De volta ao seu exemplo:
string text = "123";
double value = (double)text;
Isso não será compilado porque o compilador não pode converter texto em números. O texto pode conter qualquer caractere, não apenas números e isso é demais, em C #, mesmo para uma conversão explícita (mas pode ser permitido em outro idioma).
Objetos
As conversões de ponteiros (para objetos) podem falhar se os tipos não estiverem relacionados, por exemplo, este código não compilará (porque o compilador sabe que não há conversão possível):
string text = (string)AppDomain.Current;
Exception exception = (Exception)"abc";
Este código será compilado, mas pode falhar em tempo de execução (depende do tipo efetivo de objetos fundidos) com um InvalidCastException
:
object obj = GetNextObjectFromInput();
string text = (string)obj;
obj = GetNextObjectFromInput();
Exception exception = (Exception)obj;
Conversões
Então, finalmente, se casts são conversões, por que precisamos de classes como Convert
? Ignorando as diferenças sutis que vêm da Convert
implementação e IConvertible
implementações, na verdade, porque em C # com um elenco você diz ao compilador:
confie em mim, esse tipo é aquele mesmo que você não possa saber agora, deixe-me fazer isso e você verá.
-ou-
não se preocupe, não me importo se algo se perderá nesta conversão.
Para qualquer outra coisa, uma operação mais explícita é necessária (pense nas implicações dos casts fáceis , é por isso que C ++ introduziu uma sintaxe longa, detalhada e explícita para eles). Isso pode envolver uma operação complexa (para string
-> double
conversão será necessária uma análise). Uma conversão para string
, por exemplo, sempre é possível (via ToString()
método), mas pode significar algo diferente do que você espera, então deve ser mais explícito do que um elenco ( mais você escreve, mais você pensa no que está fazendo ).
Essa conversão pode ser feita dentro do objeto (usando instruções IL conhecidas para isso), usando operadores de conversão personalizados (definidos na classe para lançar) ou mecanismos mais complexos ( TypeConverter
s ou métodos de classe, por exemplo). Você não está ciente do que vai acontecer para fazer isso, mas está ciente de que pode falhar (é por isso que IMO, quando uma conversão mais controlada é possível, você deve usá-lo). No seu caso, a conversão simplesmente analisará o string
para produzir um double
:
double value = Double.Parse(aStringVariable);
É claro que isso pode falhar, então se você fizer isso, você sempre deve capturar a exceção que pode lançar ( FormatException
). Está fora do assunto aqui, mas quando um TryParse
estiver disponível, você deve usá-lo (porque semanticamente você diz que pode não ser um número e é ainda mais rápido ... falhar).
As conversões no .NET podem vir de vários lugares, TypeConverter
conversões implícitas / explícitas com operadores de conversão definidos pelo usuário, implementação IConvertible
e métodos de análise (esqueci de algo?). Dê uma olhada no MSDN para obter mais detalhes sobre eles.
Para terminar esta longa resposta, apenas algumas palavras sobre os operadores de conversão definidos pelo usuário. É apenas o açúcar para deixar o programador usar um elenco de converter um tipo para outro. É um método dentro de uma classe (aquela que será lançada) que diz "ei, se ele quiser converter este tipo para aquele tipo então eu posso fazer isso". Por exemplo:
float? maybe = 10;
float sure1 = (float)maybe;
float sure2 = maybe.Value;
Nesse caso, é explícito porque pode falhar, mas isso é deixado para a implementação (mesmo que haja diretrizes sobre isso). Imagine que você escreva uma classe de string personalizada como esta:
EasyString text = "123";
double value = (string)text;
Em sua implementação, você pode decidir "tornar a vida do programador mais fácil" e expor essa conversão por meio de um elenco (lembre-se de que é apenas um atalho para escrever menos). Alguns idiomas podem até permitir isso:
double value = "123";
Permitindo a conversão implícita para qualquer tipo (a verificação será feita em tempo de execução). Com as opções adequadas, isso pode ser feito, por exemplo, em VB.NET. É apenas uma filosofia diferente.
O que posso fazer com eles?
Portanto, a questão final é quando você deve usar um ou outro. Vamos ver quando você pode usar um elenco explícito:
- Conversões entre tipos básicos.
- Conversões de
object
para qualquer outro tipo (isso também pode incluir unboxing).
- Conversões de uma classe derivada para uma classe base (ou para uma interface implementada).
- Conversões de um tipo para outro por meio de operadores de conversão personalizados.
Apenas a primeira conversão pode ser feita com Convert
as outras que você não tem escolha e precisa usar um elenco explícito.
Vamos ver quando você pode usar Convert
:
- Conversões de qualquer tipo base para outro tipo base (com algumas limitações, consulte MSDN ).
- Conversões de qualquer tipo que implemente
IConvertible
em qualquer outro tipo (compatível).
- Conversões de / para uma
byte
matriz de / para uma string.
Conclusões
O IMO Convert
deve ser usado sempre que você souber que uma conversão pode falhar (por causa do formato, por causa do intervalo ou porque pode ser incompatível), mesmo se a mesma conversão puder ser feita com um elenco (a menos que algo mais esteja disponível). Deixa claro para quem vai ler seu código qual é sua intenção e que ele pode falhar (simplificando a depuração).
Para todo o resto você precisa usar um elenco, sem escolha, mas se outro método melhor estiver disponível, eu sugiro que você o use. Em seu exemplo, uma conversão de string
para double
é algo que (especialmente se o texto vem do usuário) muitas vezes falhará, então você deve torná-lo o mais explícito possível (além disso, você obtém mais controle sobre isso), por exemplo, usando um TryParse
método.
Edit: qual é a diferença entre eles?
De acordo com a pergunta atualizada e mantendo o que escrevi antes (sobre quando você pode usar um elenco em comparação com quando você pode / deve usar Convert
), o último ponto a esclarecer é se há diferença entre eles (além disso, Convert
usa IConvertible
e IFormattable
interfaces para que possa realizar operações não permitido com moldes).
A resposta curta é sim, eles se comportam de maneira diferente . Eu vejo a Convert
classe como uma classe de métodos auxiliares com tanta frequência que fornece algum benefício ou comportamentos ligeiramente diferentes. Por exemplo:
double real = 1.6;
int castedInteger = (int)real;
int convertedInteger = Convert.ToInt32(real);
Muito diferente, certo? O elenco trunca (é o que todos esperamos), mas Convert
executa um arredondamento para o número inteiro mais próximo (e isso pode não ser esperado se você não estiver ciente disso). Cada método de conversão apresenta diferenças, portanto, uma regra geral não pode ser aplicada e devem ser vistos caso a caso ... 19 tipos de base para converter para qualquer outro tipo ... a lista pode ser bem longa, muito melhor consultar o MSDN caso por caso!