c# - procesos - tipos de moldes para fundicion
Diferencia entre fundición y uso del método Convert.To() (9)
Tengo una función que arroja un double
en string
valores de string
.
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 genera sin ningún error.
¿Cuál es la diferencia entre el casting y el uso del método Convert.To()
? ¿Por qué el lanzamiento arroja una Exception
y el uso de Convert.To()
no?
Casting es una forma de decirle al compilador: "Sé que piensas que esta variable es un Bar, pero 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 se obtiene una excepción. (Específicamente un System.InvalidCastException
)
La conversión, por otro lado, es una forma de decir: "Si me das un objeto de tipo Bar, puedo crear un nuevo objeto Foo que represente lo que hay en ese objeto Bar. No cambiaré el objeto original, ganó". Para tratar el objeto original de manera diferente, creará algo nuevo que se base únicamente en algún otro valor . En cuanto a cómo lo hará, podría ser cualquier cosa. En el caso de Convert.ToDouble
, terminará llamando a Double.Parse
que tiene todo tipo de lógica compleja para determinar qué tipos de cadenas representan qué valores numéricos. Podría escribir su propio método de conversión para que las cadenas mapeadas se dupliquen de manera diferente (quizás para sustentar una 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 realmente 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 h ow para mapear (como un ejemplo) una string
a un double
.
Entonces, ¿cuándo te conviertes y cuándo echas? En ambos casos tenemos una variable de un tipo, digamos A, y queremos tener una variable de tipo B. Si nuestro objeto A en realidad, en realidad, debajo del capó, es una B, entonces lanzamos. Si no es realmente una B, entonces necesitamos convertirla, y definir cómo se supone que el programa obtiene una B de una A.
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.
Puede convertir un double
en una string
como esta
double d = 5;
string s = d.ToString(); // -> "5"
// Or by specifying a format
string formatted = d.ToString("N2"); // -> "5.00"
Puede convertir una string
en un double
de varias maneras (aquí solo dos):
string s = "5";
double d = Double.Parse(s); // Throws an exception if s does not contain a valid number
O la manera segura
string s = "5";
double d;
if (Double.TryParse(s, out d)) {
Console.WriteLine("OK. Result = {0}", d);
} else {
Console.WriteLine("oops!");
}
Desde MSDN
:
Conversiones explícitas (conversiones): las conversiones explícitas requieren un operador de conversión. Se requiere conversión cuando la información puede perderse en la conversión o cuando la conversión puede no tener éxito por otros motivos. Los ejemplos típicos incluyen la conversión numérica a un tipo que tiene menos precisión o un rango más pequeño, y la conversión de una instancia de clase base a una clase derivada.
Considere el siguiente ejemplo:
double a = 2548.3;
int b;
b = (int)a; //2548 --> information (.3) lost in the conversion
Y también:
Un elenco es una forma de informar explícitamente al compilador de que tiene la intención de realizar la conversión y de que usted sabe que podría ocurrir la pérdida de datos.
Podría usar la clase System.Convert
cuando quiera convertir entre tipos no compatibles . La principal diferencia entre el lanzamiento y la conversión es la compilación y el tiempo de ejecución . Las excepciones de conversión de tipos se muestran en tiempo de ejecución , es decir, una conversión de tipo que falla en tiempo de ejecución provocará que se InvalidCastException
una InvalidCastException
.
a
realmente es tipo b
y si es así, el proyecto se genera sin ningún error como este ejemplo: double s = 2;
int a = (int) s;
Pero en la conversión le estás diciendo al compilador que hay una manera de crear un nuevo objeto a partir de a
de tipo b
, hazlo y crea proyectos sin ningún error, pero como dije si el tipo de conversión falla en el tiempo de ejecución, provocará una InvalidCastException
que se lanzará .
Por ejemplo, el código siguiente nunca se compila porque el compilador detecta que no puede emitir una expresión de tipo DateTime
para escribir int
:
DateTime s = DateTime.Now;
int a = (int)(s);
Pero este está compilado con éxito:
DateTime s = DateTime.Now;
int a = Convert.ToInt32(s);
Pero en tiempo de ejecución obtendrá InvalidCastException
que dice:
Lanzamiento inválido de ''DateTime'' a ''Int32''.
El método Convert.Double
simplemente llama internamente al Double.Parse(string)
.
Ni el tipo de String
ni el tipo Double
definen una conversión explícita / implícita entre los dos tipos, por lo que la conversión siempre fallará.
El método Double.Parse
examinará cada carácter en la string
y construirá un valor numérico basado en los valores de los caracteres en la string
. Si alguno de los caracteres no es válido, el método Parse
falla (lo que hace que el método Convert.Double
falle).
En su ejemplo, intenta 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 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 hubiera sido 5.00 (pérdida de 0.25 debido a la Conversión a Int64)
Para responder a su pregunta sobre el lanzamiento frente a la conversión.
Tu elenco (un elenco explícito) no cumple los requisitos para un elenco explícito. el valor que intentas lanzar con el operador de conversión no es válido (es decir, no es integral).
Visite esta página de MSDN para conocer las reglas de conversión / conversión
Incluso si los ves de algún modo como equivalentes, son completamente diferentes en su propósito. Primero tratemos de definir qué es un elenco:
Casting 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 a menudo el elenco tiene la misma sintaxis de una conversión, por lo que la pregunta debería ser cuando el idioma permite un reparto (implícito o explícito) y cuándo se 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). También un yeso es reversible mientras que la conversión puede no serlo.
Este tema es bastante amplio, así que intentemos restringirnos un poco al excluir del juego a los operadores de transmisión personalizados.
Moldes implícitos
En C # un yeso está implícito 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ás ninguna información (simplemente amplías el tipo). A la inversa, no se permite la conversión implícita porque, independientemente de sus valores reales (ya que solo se pueden verificar en el tiempo de ejecución), durante la conversión puede perder algo de información. Por ejemplo, este código no se compilará porque un double
puede contener (y de hecho lo hace) un valor no representable con un float
:
double bigReal = Double.MaxValue;
float tinyReal = bigReal;
Objetos
En el caso de un objeto (un puntero) el molde siempre está implícito cuando el compilador puede estar seguro de que el tipo de origen es una clase derivada (o implementa) el tipo de la clase objetivo, 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 (se deriva de) Exception
por lo que el molde está implícito. No se pierde ninguna información porque los objetos no cambian sus tipos (esto es diferente con las struct
y los tipos primitivos porque con un molde se crea un nuevo objeto de otro tipo ), lo que cambia es su vista de ellos.
Moldes explícitos
Un elenco es explícito cuando el compilador no realiza la conversión implícitamente y luego debe usar el operador de conversión. Usualmente significa que:
- Puede perder información o datos, por lo que debe tenerlo en cuenta.
- La conversión puede fallar (porque no puede convertir un tipo al otro) así que, nuevamente, debe ser consciente de lo que está haciendo.
Tipos primitivos
Se requiere un lanzamiento explícito para los 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 caen dentro del rango float
, 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, de nuevo, debe ser explícita para que esté al tanto y puede hacer una comprobación (en el ejemplo, el valor es constante pero puede provenir de algunos cálculos en tiempo de ejecución o E / S). De vuelta a tu ejemplo:
string text = "123";
double value = (double)text;
Esto no compilará porque el compilador no puede convertir texto a números. El texto puede contener cualquier carácter, no solo números y esto es demasiado, en C #, incluso para un molde explícito (pero puede estar permitido en otro idioma).
Objetos
La conversión de punteros (a objetos) puede 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 el tiempo de ejecución (depende del tipo efectivo de objetos casted) con una InvalidCastException
:
object obj = GetNextObjectFromInput();
string text = (string)obj;
obj = GetNextObjectFromInput();
Exception exception = (Exception)obj;
Conversiones
Entonces, finalmente, si los moldes son una conversión, ¿por qué necesitamos clases como Convert
? Ignorando las sutiles diferencias que surgen de la implementación Convert
y IConvertible
implementaciones IConvertible
, en realidad porque en C # con un molde le dices al compilador:
confía en mí, este tipo es de ese tipo, incluso si no puedes saberlo ahora, déjame hacerlo y ya lo verás.
-o-
no te preocupes, no me importa que algo se pierda en esta conversión.
Para cualquier otra cosa, se necesita una operación más explícita (piense en las implicaciones de los moldes fáciles , por eso C ++ introdujo una sintaxis larga, detallada y explícita para ellos). Esto puede implicar una operación compleja (para string
-> conversión double
será necesario un análisis). La conversión a string
, por ejemplo, siempre es posible (a través del método ToString()
) pero puede significar algo diferente de lo que esperas, así que debe ser más explícito que un molde ( más escribes, más piensas en lo que estás haciendo )
Esta conversión se puede realizar dentro del objeto (utilizando las instrucciones conocidas de IL para eso), utilizando operadores de conversión personalizados (definidos en la clase a TypeConverter
) o mecanismos más complejos ( TypeConverter
o métodos de clase, por ejemplo). No está al tanto de lo que ocurrirá al hacerlo, pero sabe que puede fallar (es por eso que IMO, cuando es posible una conversión más controlada , debería usarla). En su caso, la conversión simplemente analizará la string
para producir un double
:
double value = Double.Parse(aStringVariable);
Por supuesto, esto puede fallar, por lo tanto, si lo hace, siempre debe detectar la excepción que puede arrojar ( FormatException
). Está fuera de tema aquí, pero si cuando hay un TryParse
disponible, entonces debe usarlo (porque semánticamente usted dice que puede no ser un número y es incluso más rápido ... fallar).
Las conversiones en .NET pueden provenir de muchos lugares, TypeConverter
, TypeConverter
implícitas / explícitas con operadores de conversión definidos por el usuario, implementación de IConvertible
y métodos de análisis (¿Olvidé algo?). Eche un vistazo en MSDN para obtener más detalles sobre ellos.
Para finalizar esta larga respuesta, solo unas pocas palabras sobre los operadores de conversión definidos por el usuario. Simplemente es agotador permitir que el programador use un elenco para convertir un tipo en otro. Es un método dentro de una clase (el que será lanzado) que dice "hey, si él / ella quiere convertir este tipo a ese tipo, entonces yo 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 aplica a la implementación (incluso si hay directrices sobre esto). Imagine que escribe 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 "facilitar la vida del programador" y exponer esta conversión a través de un elenco (recuerde que es solo un atajo para escribir menos). Algunos idiomas incluso pueden permitir esto:
double value = "123";
Permitir 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 cuando deberías usar uno u otro. Veamos cuando puedes usar un elenco explícito:
- Conversiones entre tipos base.
- Conversiones de
object
a cualquier otro tipo (esto puede incluir unboxing también). - Conversiones de una clase derivada a una clase base (o a una interfaz implementada).
- Conversiones de un tipo a otro a través de operadores de conversión personalizados.
Solo la primera conversión se puede hacer con Convert
por lo que para los demás no tiene otra opción y debe usar un molde explícito.
Veamos ahora cuando puedes usar Convert
:
- Conversiones de cualquier tipo base a otro tipo base (con algunas limitaciones, consulte MSDN ).
- Conversiones de cualquier tipo que implementa
IConvertible
a cualquier otro tipo (compatible). - Conversiones de / a una matriz de
byte
a / desde una cadena.
Conclusiones
IMO Convert
debe usar cada vez que sepa que una conversión puede fallar (debido al formato, debido al alcance o porque puede no ser compatible), incluso si la misma conversión se puede hacer con un molde (a menos que haya algo más disponible). Deja 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, necesitas usar un yeso, no hay otra opción, pero si hay otro método mejor disponible, te sugiero que lo uses. 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, tendrá más control sobre él), por ejemplo, utilizando un método TryParse
.
Editar: ¿cuál es la diferencia entre ellos?
De acuerdo con la pregunta actualizada y manteniendo lo que escribí antes (sobre cuándo puedes usar un elenco en comparación con cuándo puedes / debes usar Convert
), el último punto para aclarar es si hay diferencia entre ellos (además, Convert
usa las interfaces IConvertible
e IFormattable
, puede realizar operaciones no permitidas con moldes).
La respuesta corta es sí, se comportan diferente . Veo a la clase Convert
como una clase de métodos de ayuda tan a menudo que proporciona algunos beneficios o comportamientos ligeramente diferentes. Por ejemplo:
double real = 1.6;
int castedInteger = (int)real; // 1
int convertedInteger = Convert.ToInt32(real); // 2
Bastante diferente, ¿verdad? Cast trunca (es lo que todos esperamos) pero Convert
realiza un redondeo al entero más cercano (y esto puede no esperarse si no estás enterado). Cada método de conversión introduce diferencias, por lo que una regla general no se puede aplicar y se deben ver caso por caso ... 19 tipos de base para convertir a cualquier otro tipo ... la lista puede ser bastante larga, es mucho mejor consultar el caso de MSDN por ¡caso!
Lanzar una cadena a un doble así no está permitido C #, que es la razón por la cual obtienes una Excepción, necesitas tener la cadena convertida ( http://msdn.microsoft.com/en-us/library/yht2cx7b.aspx que muestra rutas de conversión aceptables). Esto es simplemente porque una cadena no necesariamente va a contener datos numéricos, pero los diversos tipos numéricos lo harán (salvo valores nulos). Un 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, arrojará una excepción.
Para convertirlo, tienes varias opciones. Usaste el método Convert
en tu pregunta, hay Parse
que es en gran medida similar a Convert
, pero también deberías mirar TryParse que te 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 intenta Convert
o Parse
una cadena no numérica.
double varDouble = (double)variable
asume que la variable
ya es doble. Si la variable
no es un doble (es una cadena), esto fallará. double varDouble = Convert.ToDouble(variable)
hace como dice - convierte. Si puede analizar o extraer un doble de una variable
, lo hará.
Double.Parse
o Double.TryParse
porque indica más claramente lo que se supone que está sucediendo. Estás empezando con una cadena y esperando que sea convertible en un doble. Si hay alguna duda, use TryParse
.
Si variable
es un argumento de método, cambie el tipo a double. Haga que el que llama sea responsable de proporcionar el tipo correcto. De esa forma, el compilador hace el trabajo por usted.
string variable = "5.00";
double varDouble = (double)variable;
Por encima de la conversión, simplemente no está permitido por el lenguaje. 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í
¿Y cómo se diferencia esto a Convert.ToDouble ()?
Cuando proyecta un tipo, la estructura de datos no cambia. Bueno, en el caso de la conversión de valores numéricos, puede perder algunos bits u obtener pocos bits adicionales. Pero todavía estás trabajando con un número. Solo está cambiando la cantidad de memoria tomada por ese número. Eso es lo suficientemente seguro como para que el compilador haga todo lo necesario.
Pero cuando intentas lanzar cadena a un número, no puedes hacer eso porque no es suficiente para cambiar la cantidad de memoria tomada por variable. Por ejemplo, 5.00 como una 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 toma los primeros N (4 para el doble ?, no seguro) los 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, calculará el dígito que representa y creará un número doble para usted, si string representa un número. Los lenguajes como PHP llamarán, en términos generales, Convert.ToDouble en segundo plano. Pero C #, como un lenguaje estáticamente tipado, no lo hará por usted. Esto le permite estar seguro de que cualquier operación es segura y no obtendrá algo inesperado haciendo algo como:
double d = (double)"zzzz"