Incluso si puede verlos de alguna manera como equivalentes, tienen un propósito completamente diferente. Primero intentemos definir qué es un elenco:
La conversión es la acción de cambiar una entidad de un tipo de datos a otro.
Es un poco genérico y de alguna manera es equivalente a una conversión porque un elenco a menudo tiene la misma sintaxis de una conversión, por lo que la pregunta debería ser cuándo el lenguaje permite un elenco (implícito o explícito) y cuándo debe usar un ( más) conversión explícita?
Permítanme primero dibujar una línea simple entre ellos. Formalmente (incluso si es equivalente para la sintaxis del lenguaje) un elenco cambiará el tipo mientras que una conversión cambiará / puede cambiar el valor (eventualmente junto con el tipo). Además, un yeso es reversible mientras que una conversión puede no serlo.
Este tema es bastante amplio, así que intentemos reducirlo un poco excluyendo a los operadores de lanzamiento personalizados del juego.
Repartos implícitos
En C #, una conversión está implícita cuando no perderá ninguna información (tenga en cuenta que esta verificación se realiza con tipos y no con sus valores reales ).
Tipos primitivos
Por ejemplo:
int tinyInteger = 10;
long bigInteger = tinyInteger;
float tinyReal = 10.0f;
double bigReal = tinyReal;
Estas conversiones son implícitas porque durante la conversión no perderá ninguna información (solo amplía el tipo). No se permite la conversión implícita viceversa porque, independientemente de sus valores reales (porque solo se pueden verificar en tiempo de ejecución), durante la conversión puede perder algo de información. Por ejemplo, este código no se compilará porque a double
puede contener (y de hecho lo hace) un valor no representable con a float
:
double bigReal = Double.MaxValue;
float tinyReal = bigReal;
Objetos
En el caso de un objeto (un puntero a), la conversión siempre está implícita cuando el compilador puede estar seguro de que el tipo de origen es una clase derivada (o implementa) el tipo de la clase de destino, por ejemplo:
string text = "123";
IFormattable formattable = text;
NotSupportedException derivedException = new NotSupportedException();
Exception baseException = derivedException;
En este caso, el compilador sabe que string
implementa IFormattable
y que NotSupportedException
es (deriva de) Exception
por lo que la conversión es implícita. No se pierde información porque los objetos no cambian sus tipos (esto es diferente con struct
sy tipos primitivos porque con un reparto creas un nuevo objeto de otro tipo ), lo que cambia es tu vista de ellos.
Repartos explícitos
Una conversión es explícita cuando el compilador no realiza la conversión implícitamente y luego debe usar el operador de conversión. Por lo general, significa que:
- Puede perder información o datos, por lo que debe ser consciente de ello.
- La conversión puede fallar (porque no puede convertir un tipo a otro) así que, nuevamente, debe estar consciente de lo que está haciendo.
Tipos primitivos
Se requiere una conversión explícita para tipos primitivos cuando durante la conversión puede perder algunos datos, por ejemplo:
double precise = Math.Cos(Math.PI * 1.23456) / Math.Sin(1.23456);
float coarse = (float)precise;
float epsilon = (float)Double.Epsilon;
En ambos ejemplos, incluso si los valores se encuentran dentro del float
rango, perderá información (en este caso precisión), por lo que la conversión debe ser explícita. Ahora prueba esto:
float max = (float)Double.MaxValue;
Esta conversión fallará, así que, nuevamente, debe ser explícita para que lo sepa y pueda hacer una verificación (en el ejemplo, el valor es constante pero puede provenir de algunos cálculos en tiempo de ejecución o E / S). Volviendo a tu ejemplo:
string text = "123";
double value = (double)text;
Esto no se compilará porque el compilador no puede convertir texto en números. El texto puede contener cualquier carácter, no solo números y esto es demasiado, en C #, incluso para una conversión explícita (pero puede estar permitido en otro idioma).
Objetos
Las conversiones de punteros (a objetos) pueden fallar si los tipos no están relacionados, por ejemplo, este código no se compilará (porque el compilador sabe que no hay conversión posible):
string text = (string)AppDomain.Current;
Exception exception = (Exception)"abc";
Este código se compilará pero puede fallar en tiempo de ejecución (depende del tipo efectivo de objetos fundidos) con un InvalidCastException
:
object obj = GetNextObjectFromInput();
string text = (string)obj;
obj = GetNextObjectFromInput();
Exception exception = (Exception)obj;
Conversiones
Entonces, finalmente, si los elencos son conversiones, ¿por qué necesitamos clases como Convert
? Ignorando las sutiles diferencias que provienen de la Convert
implementación y las IConvertible
implementaciones, en realidad porque en C # con un elenco le dices al compilador:
créame, este tipo es de ese tipo, incluso si no puede saberlo ahora, déjeme hacerlo y lo verá.
-o-
no se preocupe, no me importa si algo se perderá en esta conversión.
Para cualquier otra cosa , se necesita una operación más explícita (piense en las implicaciones de conversiones fáciles , por eso C ++ introdujo una sintaxis larga, detallada y explícita para ellos). Esto puede implicar una operación compleja (para string
-> double
conversión se necesitará un análisis sintáctico). Una conversión a string
, por ejemplo, siempre es posible (a través del ToString()
método) pero puede significar algo diferente de lo que esperas, por lo que debe ser más explícito que un elenco (cuanto más escribes, más piensas en lo que estás haciendo ).
Esta conversión se puede hacer dentro del objeto (usando instrucciones de IL conocidas para eso), usando operadores de conversión personalizados (definidos en la clase a emitir) o mecanismos más complejos ( TypeConverter
s o métodos de clase, por ejemplo). No está al tanto de lo que sucederá para hacer eso, pero sabe que puede fallar (por eso, en mi opinión, cuando es posible una conversión más controlada , debe usarla). En su caso, la conversión simplemente analizará el string
para producir un double
:
double value = Double.Parse(aStringVariable);
Por supuesto, esto puede fallar, por lo que si lo hace, siempre debe detectar la excepción que puede lanzar ( FormatException
). Está fuera de tema aquí, pero cuando a TryParse
está disponible, debe usarlo (porque semánticamente dice que puede que no sea un número y es aún más rápido ... fallar).
Las conversiones en .NET pueden provenir de muchos lugares, TypeConverter
conversiones implícitas / explícitas con operadores de conversión definidos por el usuario, implementación IConvertible
y métodos de análisis (¿olvidé algo?). Eche un vistazo a MSDN para obtener más detalles sobre ellos.
Para terminar esta larga respuesta, solo unas pocas palabras sobre los operadores de conversión definidos por el usuario. Es simplemente genial dejar que el programador use un molde para convertir un tipo en otro. Es un método dentro de una clase (el que será lanzado) que dice "oye, si él / ella quiere convertir este tipo a ese tipo, entonces puedo hacerlo". Por ejemplo:
float? maybe = 10;
float sure1 = (float)maybe;
float sure2 = maybe.Value;
En este caso, es explícito porque puede fallar, pero esto se deja a la implementación (incluso si hay pautas al respecto). Imagina que escribes una clase de cadena personalizada como esta:
EasyString text = "123";
double value = (string)text;
En su implementación, puede decidir "hacer la vida del programador más fácil" y exponer esta conversión a través de un reparto (recuerde que es solo un atajo para escribir menos). Algunos idiomas incluso pueden permitir esto:
double value = "123";
Permitiendo la conversión implícita a cualquier tipo (la verificación se realizará en tiempo de ejecución). Con las opciones adecuadas, esto se puede hacer, por ejemplo, en VB.NET. Es solo una filosofía diferente.
¿Qué puedo hacer con ellos?
Entonces, la pregunta final es cuándo debe usar uno u otro. Veamos cuándo puedes usar un elenco explícito:
- Conversiones entre tipos de base.
- Conversiones de
object
a cualquier otro tipo (esto también puede incluir el desembalaje).
- Conversiones de una clase derivada a una clase base (o a una interfaz implementada).
- Conversiones de un tipo a otro mediante operadores de conversión personalizados.
Solo se puede realizar la primera conversión, por Convert
lo que para las demás no tiene otra opción y necesita usar un elenco explícito.
Veamos ahora cuándo puedes usar Convert
:
- Conversiones de cualquier tipo base a otro tipo base (con algunas limitaciones, consulte MSDN ).
- Conversiones de cualquier tipo que se implemente
IConvertible
a cualquier otro tipo (admitido).
- Conversiones de / a una
byte
matriz a / desde una cadena.
Conclusiones
La OMI Convert
debe usarse cada vez que sepa que una conversión puede fallar (debido al formato, debido al rango o porque puede no ser compatible), incluso si la misma conversión se puede hacer con una conversión (a menos que haya algo más disponible). Deja en claro quién leerá su código cuál es su intención y que puede fallar (simplificando la depuración).
Para todo lo demás, necesita usar un yeso, no hay opción, pero si hay otro método mejor disponible, le sugiero que lo use. En su ejemplo, una conversión de string
a double
es algo que (especialmente si el texto proviene del usuario) muy a menudo fallará, por lo que debe hacerlo lo más explícito posible (además, obtiene más control sobre él), por ejemplo, utilizando un TryParse
método.
Editar: ¿cuál es la diferencia entre ellos?
De acuerdo con la pregunta actualizada y manteniendo lo que escribí antes (sobre cuándo puede usar un molde en comparación con cuándo puede / debe usar Convert
), el último punto a aclarar es si hay diferencias entre ellos (además, Convert
usos IConvertible
e IFormattable
interfaces para que pueda realizar operaciones no permitido con yesos).
La respuesta corta es sí, se comportan de manera diferente . Veo la Convert
clase como una clase de métodos auxiliares con tanta frecuencia que proporciona algún beneficio o comportamientos ligeramente diferentes. Por ejemplo:
double real = 1.6;
int castedInteger = (int)real;
int convertedInteger = Convert.ToInt32(real);
Bastante diferente, ¿verdad? El reparto se trunca (es lo que todos esperamos) pero Convert
realiza un redondeo al número entero más cercano (y esto puede no ser esperado si no lo sabe). Cada método de conversión introduce diferencias, por lo que no se puede aplicar una regla general y deben verse caso por caso ... 19 tipos base para convertir a cualquier otro tipo ... la lista puede ser bastante larga, mucho mejor consultar el caso de MSDN por ¡caso!