Tengo una función que proyecta una double
en string
valores.
string variable = "5.00";
double varDouble = (double)variable;
Se registró un cambio de código y el proyecto se compila con el error: System.InvalidCastException: Specified cast is not valid.
Sin embargo, después de hacer lo siguiente ...
string variable = "5.00";
double varDouble = Convert.ToDouble(variable);
... el proyecto se construye sin errores.
¿Cuál es la diferencia entre lanzar y usar el Convert.To()
método? ¿Por qué lanzar un lanzamiento Exception
y usar el Convert.To()
no?
Respuestas:
Incluso si puede verlos de alguna manera como equivalentes, tienen un propósito completamente diferente. Primero intentemos definir qué es un elenco:
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 afloat
:// won't compile! 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
implementaIFormattable
y queNotSupportedException
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 construct
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:
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:
// won't compile! 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):
// won't compile! 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 laConvert
implementación y lasIConvertible
implementaciones, en realidad porque en C # con un elenco le dices al compilador:-o-
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 astring
, por ejemplo, siempre es posible (a través delToString()
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á elstring
para producir undouble
: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 aTryParse
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ónIConvertible
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; // Equals to Nullable<float> maybe = 10; float sure1 = (float)maybe; // With cast float sure2 = maybe.Value; // Without cast
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"; // Implicit from string double value = (string)text; // Explicit to double
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:
object
a cualquier otro tipo (esto también puede incluir el desembalaje).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
:IConvertible
a cualquier otro tipo (admitido).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
adouble
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 unTryParse
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
usosIConvertible
eIFormattable
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; // 1 int convertedInteger = Convert.ToInt32(real); // 2
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!fuente
Difference between casting and using the Convert.To() method
. De lo contrario, respuesta muy completa. (Espero que mi pregunta se vuelva a abrir ...)double
valores que no representan números enteros deberían ser "convertibles" aint
. Un elenco parecería el paradigma apropiado en los casos en los que, por ejemplo, uno está recuperandoInt32
valores de undouble[]
que contiene una combinación de números reales yInt32
valores que se han convertido endouble
[un intento de convertir un valor que no es representable con precisión enint32
indicaría una condición inesperada y debería activar una excepción], pero creo que cuando uno quiere una conversión con pérdidas debería ser específico sobre la forma que quiere.object o = 123; var l = Convert.ToInt64(o); var i = (long) (int) o; var f = (long) o // InvalidCastException
float
->int
) sino una coacción . Un elenco podría ser, por ejemplo,DerivedClass
->BaseClass
. Es confuso porque en C # usamos la misma palabra (y operador) para ambos, pero en realidad son cosas distintas. Una definición formal para distinguirlos es un poco más complicada de lo que escribí.La transmisión es una forma de decirle al compilador: "Sé que piensas que esta variable es una barra, pero resulta que yo sé más que tú; el objeto es en realidad un Foo, así que déjame tratarlo como si fuera un Foo de ahora en adelante ". Luego, en tiempo de ejecución, si el objeto real resultó ser realmente un Foo, entonces su código funciona, si resulta que el objeto no era un Foo en absoluto, entonces obtiene una excepción. (Específicamente un
System.InvalidCastException
.)Por otro lado, convertir es una forma de decir: "Si me da un objeto de tipo Bar, puedo crear un objeto Foo nuevo que represente lo que hay en ese objeto Bar. No cambiaré el objeto original, ganó". Si trata el objeto original de manera diferente, creará algo nuevo que simplemente se basa en algún otro valor . En cuanto a cómo lo hará, podría ser cualquier cosa. En el caso de
Convert.ToDouble
que termine llamandoDouble.Parse
que tiene todo tipo de lógica compleja para determinar qué tipos de cadenas representan qué valores numéricos. Puede escribir su propio método de conversión que asigne cadenas a dobles de manera diferente (quizás para admitir alguna convención completamente diferente para mostrar números, como números romanos o lo que sea). Una conversión podría hacer cualquier cosa, pero la idea es que no le está pidiendo al compilador que haga nada por usted; usted es quien escribe el código para determinar cómo crear el nuevo objeto porque el compilador, sin su ayuda, no tiene forma de saber cómo mapear (como ejemplo) astring
a adouble
.Entonces, ¿cuándo te conviertes y cuándo lanzas? En ambos casos tenemos alguna variable de un tipo, digamos A, y queremos tener una variable de tipo B. Si nuestro objeto A realmente, en realidad, bajo el capó, es una B, entonces lanzamos. Si no es realmente una B, entonces debemos convertirla y definir cómo se supone que el programa obtiene una B de una A.
fuente
foreach
). Fuera de esas excepciones, las conversiones son explícitas por definición .De
MSDN
:Considere el siguiente ejemplo:
double a = 2548.3; int b; b = (int)a; //2548 --> information (.3) lost in the conversion
Y también:
Puede usar
System.Convert
class cuando quiera convertir entre tipos no compatibles . La principal diferencia entre la conversión y la conversión es la compilación y el tiempo de ejecución . Las excepciones de conversión de tipos aparecen en tiempo de ejecución , es decir, un tipo de conversión que falla en tiempo de ejecución provocaráInvalidCastException
que se lance un.Conclusión: en la conversión le está diciendo al compilador que
a
es realmente tipob
y, de ser así, el proyecto se construye sin errores como este ejemplo:double s = 2; int a = (int) s;
Pero en la conversión que está diciendo al compilador que hay una manera de crear un nuevo objeto a partir
a
del tipob
, por favor hacerlo y proyecto se basa sin ningún error, pero como he dicho si el tipo de conversión falla en tiempo de ejecución, se producirá unaInvalidCastException
de ser arrojado .Por ejemplo, el código siguiente nunca se compila porque el compilador detecta que no puede convertir una expresión de tipo
DateTime
a tipoint
:DateTime s = DateTime.Now; int a = (int)(s);
Pero este se compila con éxito:
DateTime s = DateTime.Now; int a = Convert.ToInt32(s);
Pero en el tiempo de ejecución obtendrá lo
InvalidCastException
que dice:fuente
El
Convert.Double
método en realidad solo llama internamente alDouble.Parse(string)
método.Ni el
String
tipo ni elDouble
tipo definen una conversión explícita / implícita entre los dos tipos, por lo que la conversión siempre fallará.El
Double.Parse
método observará cada carácter delstring
y generará un valor numérico basado en los valores de los caracteres delstring
. Si alguno de los caracteres no es válido, elParse
método falla (lo que hace que elConvert.Double
método también falle).fuente
Convert.ToDouble()
miraría más allá de los bytes y consideraría los datos?En su ejemplo, está intentando convertir una cadena en un doble (tipo no integral).
Se requiere una conversión explícita para que funcione.
Y debo señalar que podría haber usado en
Convert.ToDouble
lugar de,Convert.ToInt64
ya que puede perder las partes fraccionarias del valor doble cuando convierte a un int.si su variable tiene el valor "5.25" varDouble habría sido 5.00 (pérdida de 0.25 debido a la conversión a Int64)
Para responder a su pregunta sobre la transmisión frente a la conversión.
Tu elenco (un elenco explícito) no cumple con los requisitos para un elenco explícito. el valor que está intentando convertir con el operador de conversión no es válido (es decir, no integral).
Visite esta página de MSDN para conocer las reglas de casting / conversiones
fuente
El casting no implica ninguna conversión, es decir, la representación interna de un valor no se modifica. Ejemplo:
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.
Puedes convertir un
double
astring
como estedouble d = 5; string s = d.ToString(); // -> "5" // Or by specifying a format string formatted = d.ToString("N2"); // -> "5.00"
Puede convertir
string
adouble
en a de varias formas (aquí solo dos de ellas):string s = "5"; double d = Double.Parse(s); // Throws an exception if s does not contain a valid number
O la forma segura
string s = "5"; double d; if (Double.TryParse(s, out d)) { Console.WriteLine("OK. Result = {0}", d); } else { Console.WriteLine("oops!"); }
fuente
Convert.ToDouble()
llamadas internasDouble.Parse()
. ¿Me conviene usarConvert.ToDouble()
overDouble.Parse()
o no y por qué?Convert.ToDouble
tiene muchas sobrecargas que aceptan diferentes tipos de entrada. La sobrecarga que aceptastring
regresa0.0
sinull
se pasa una cadena. Aparte de esto, no veo ninguna ventaja en su uso.Double.Parse()
tiene algo que ofrecer que debería considerar?Double.Parse()
es más directo queConvert.ToDouble()
. Si está seguro de que su cadena contendrá un número válido, puede usarlo con seguridad; de lo contrario, le aconsejo que lo useDouble.TryParse
.string variable = "5.00"; double varDouble = (double)variable;
La conversión anterior simplemente no está permitida por el idioma. Aquí hay una lista de conversiones explícitas para tipos numéricos: http://msdn.microsoft.com/en-us/library/yht2cx7b.aspx Como puede ver, incluso no todos los tipos numéricos se pueden convertir a otro tipo numérico
Más información sobre el casting aquí
Cuando lanza un tipo, la estructura de datos no cambia. Bueno, en caso de conversión de valores numéricos, es posible que pierda algunos bits u obtenga algunos bits 0 adicionales. Pero todavía estás trabajando con un número. Solo está cambiando la cantidad de memoria que ocupa ese número. Eso es lo suficientemente seguro para que el compilador haga todo lo necesario.
Pero cuando intenta convertir una cadena en un número, no puede hacerlo porque no es suficiente para cambiar la cantidad de memoria ocupada por la variable. Por ejemplo, 5,00como cadena es una secuencia de "números": 53 (5) 46 (.) 48 (0) 48 (0) - eso es para ASCII, pero la cadena contendrá algo similar. Si el compilador solo tomará los primeros N (4 por doble? No estoy seguro) bytes de una cadena, esa pieza contendrá un número doble completamente diferente. Al mismo tiempo, Convert.ToDouble () ejecuta un algoritmo especial que tomará cada símbolo de una cadena, averiguará el dígito que representa y hará un número doble para usted, si la cadena representa un número. Lenguajes como PHP, en términos generales, llamarán Convert.ToDouble por usted en segundo plano. Pero C #, como un lenguaje escrito estáticamente, no lo hará por usted. Esto le permite estar seguro de que cualquier operación es segura de tipos y no obtendrá algo inesperado al hacer algo como:
double d = (double)"zzzz"
fuente
No se permite convertir una cadena a un doble como ese en C #, por lo que obtiene una excepción, debe convertir la cadena ( documento de MSDN que muestra rutas de conversión aceptables). Esto se debe simplemente a que una cadena no necesariamente va a contener datos numéricos, pero los diversos tipos numéricos sí (salvo valores nulos). A
Convert
ejecutará un método que verificará la cadena para ver si se puede convertir en un valor numérico. Si puede, devolverá ese valor. Si no puede, lanzará una excepción.Para convertirlo, tiene varias opciones. Usó el
Convert
método en su pregunta,Parse
que es muy similar aConvert
, pero también debería mirar TryParse, que le permitiría hacer: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. }
Esto evita la posible excepción si lo intenta
Convert
oParse
una cadena no numérica.fuente
TryParse
overConvert
porqueTryParse
comprueba si la conversión se realiza correctamente?double varDouble = (double)variable
asume quevariable
ya es un doble. Sivariable
no es un doble (es una cadena), esto fallará.double varDouble = Convert.ToDouble(variable)
hace lo que dice: convierte. Si puede analizar o extraer un doble,variable
entonces lo hará.Segundo usando
Double.Parse
oDouble.TryParse
porque indica más claramente lo que se supone que está sucediendo. Está comenzando con una cadena y espera que sea convertible en un doble. Si tiene alguna duda, utiliceTryParse
.Si
variable
es un argumento de método, cambie el tipo a doble. Haga que la persona que llama sea responsable de proporcionar el tipo correcto. De esa forma, el compilador hace el trabajo por usted.fuente
La diferencia más importante es que si se utiliza la conversión de tipos y la conversión falla (digamos que estamos convirtiendo un valor flotante muy grande en int) no se lanzará ninguna excepción y se mostrará el valor mínimo que puede contener un int. Pero en caso de usar Convert , se lanzará una excepción para tales escenarios.
fuente