¿Lanzar lo mismo que convertir?

En el libro Learning C # de Jesse Liberty, dice: “Los objetos de un tipo se pueden convertir en objetos de otro tipo. Esto se conoce como casting“.

Si investigas el IL generado a partir del código siguiente, puedes ver claramente que la asignación casted no está haciendo lo mismo que la asignación convertida. En el primero, puede ver el boxeo / unboxing que ocurre; en este último, puede ver una llamada a un método de conversión.

Sé que al final puede ser solo una tonta diferencia semántica, pero está lanzando solo una palabra más para convertir. No quiero ser sarcástico, pero no estoy interesado en la intuición de nadie sobre esto. ¡Las opiniones no cuentan aquí! ¿Alguien puede apuntar a una referencia definitiva que confirme o niegue si el casting y la conversión son la misma cosa?

object x; int y; x = 4; y = ( int )x; y = Convert.ToInt32( x ); 

Gracias

rp

Nota agregada después del comentario de Matt sobre explícito / implícito:

No creo que implícita / explícita sea la diferencia. En el código que publiqué, el cambio es explícito en ambos casos. Una conversión implícita es lo que ocurre cuando asigna un short a un int.

Nota para Sklivvz:

Quería la confirmación de que mi sospecha de la soltura del lenguaje de Jesse Liberty (por lo demás, lúcida y clara) era correcta. Pensé que Jesse Liberty estaba siendo un poco flojo con su lenguaje. Entiendo que la conversión se enruta en la jerarquía de objetos, es decir, no se puede convertir de un entero a una cadena, pero se puede convertir desde una excepción personalizada derivada de System.Exception a una System.Exception.

Sin embargo, es interesante que cuando intentas convertir un int a una cadena, el comstackdor te dice que no puede “convertir” el valor. ¡Tal vez Jesse es más correcto de lo que pensaba!

La respuesta simple es: depende.

Para los tipos de valor, la conversión implicará convertirlo genuinamente en un tipo diferente. Por ejemplo:

 float f = 1.5f; int i = (int) f; // Conversion 

Cuando la expresión de conversión se desempaqueta, el resultado (suponiendo que funcione) generalmente es solo una copia de lo que estaba en la caja, con el mismo tipo. Sin embargo, hay excepciones: puede desempaquetar desde un recuadro int a una enumeración (con un tipo subyacente de int) y viceversa; Del mismo modo, puede desunir de un recuadro int a un Nullable .

Cuando la expresión de conversión va de un tipo de referencia a otro y no se trata de una conversión definida por el usuario, no hay conversión en lo que respecta al objeto en sí, solo el tipo de referencia “cambia”, y esa es solo la forma en que el se considera el valor, en lugar de la referencia misma (que serán los mismos bits que antes). Por ejemplo:

 object o = "hello"; string x = (string) o; // No data is "converted"; x and o refer to the same object 

Cuando se involucran las conversiones definidas por el usuario, esto generalmente implica devolver un objeto / valor diferente. Por ejemplo, podría definir una conversión a cadena para su propio tipo, y ciertamente no sería la misma información que su propio objeto. (Por supuesto, podría ser una cadena existente referida a su objeto). En mi experiencia, las conversiones definidas por el usuario generalmente existen entre tipos de valores en lugar de tipos de referencia, por lo que esto rara vez es un problema.

Todos estos cuentan como conversiones en términos de la especificación, pero no todos cuentan como convertir un objeto en un objeto de un tipo diferente. Sospecho que este es un caso en el que Jesse Liberty no tiene mucha terminología. Lo he notado en Programming C # 3.0, que acabo de leer.

¿Eso cubre todo?

¡Absolutamente no!

Convert intenta obtener un Int32 a través de “cualquier medio posible”. Cast no hace nada por el estilo. Con el reparto le está diciendo al comstackdor que trate el objeto como Int, sin conversión.

Siempre debe usar el molde cuando sepa (por diseño) que el objeto es un Int32 u otra clase que tiene un operador de fundición para Int32 (como flotar, por ejemplo).

Convert debería usarse con String o con otras clases.

Prueba esto

 static void Main(string[] args) { long l = long.MaxValue; Console.WriteLine(l); byte b = (byte) l; Console.WriteLine(b); b = Convert.ToByte(l); Console.WriteLine(b); } 

Resultado:

9223372036854775807

255

Excepción no controlada:

System.OverflowException: el valor es mayor que Byte.MaxValue o menor que Byte.MinValue en System.Convert.ToByte (valor Int64) [0x00000] en Test.Main (System.String [] args) [0x00019] en / home / marco /develop/test/Exceptions.cs:15

La mejor explicación que he visto se puede ver a continuación, seguida de un enlace a la fuente:

“… La verdad es un poco más compleja que eso. .NET proporciona tres métodos para pasar del punto A al punto B, por así decirlo.

Primero, está el reparto implícito. Este es el elenco que no requiere que hagas nada más que una tarea:

 int i = 5; double d = i; 

Estos también se llaman “ampliaciones de conversiones” y .NET le permite realizarlos sin ningún operador de conversión porque nunca podría perder información al hacerlo: el posible rango de valores válidos de un doble abarca el rango de valores válidos para una int y luego algunos, así que nunca harás esta tarea y luego descubrirás con horror que el tiempo de ejecución dejó caer unos pocos dígitos de tu valor int. Para los tipos de referencia, la regla detrás de un lanzamiento implícito es que el elenco nunca podría arrojar una InvalidCastException: para el comstackdor, es claro que el elenco siempre es válido.

Puedes crear nuevos operadores de conversión implícitos para tus propios tipos (lo que significa que puedes hacer moldes implícitos que rompen todas las reglas, si eres estúpido al respecto). La regla básica es que un lanzamiento implícito nunca puede incluir la posibilidad de perder información en la transición.

Tenga en cuenta que la representación subyacente cambió en esta conversión: un doble se representa de forma completamente diferente a un int.

El segundo tipo de conversión es un lanzamiento explícito. Se requiere un lanzamiento explícito siempre que exista la posibilidad de perder información, o existe la posibilidad de que el lanzamiento no sea válido y, por lo tanto, genere una InvalidCastException:

 double d = 1.5; int i = (int)d; 

Aquí obviamente vas a perder información: seré 1 después del reparto, entonces el 0.5 se perderá. Esto también se conoce como una conversión “angosta”, y el comstackdor requiere que incluya un molde explícito (int) para indicar que sí, usted sabe que la información puede perderse, pero no le importa.

De forma similar, con los tipos de referencia el comstackdor requiere conversiones explícitas en situaciones en las que el lanzamiento puede no ser válido en tiempo de ejecución, como una señal de que sí, usted sabe que existe un riesgo, pero usted sabe lo que está haciendo.

El tercer tipo de conversión es aquella que implica un cambio tan radical en la representación que los diseñadores no proporcionaron ni siquiera un molde explícito: lo hacen llamar a un método para hacer la conversión:

 string s = "15"; int i = Convert.ToInt32(s); 

Tenga en cuenta que no hay nada que requiera absolutamente una llamada a un método aquí. Los moldes implícitos y explícitos son también llamadas a métodos (así es como lo haces tú mismo). Los diseñadores pudieron haber creado fácilmente un operador de conversión explícito que convirtió una cadena en un int. El requisito de que llame a un método es una elección estilística más que un requisito fundamental del idioma.

El razonamiento estilístico es algo así: String-to-int es una conversión complicada con muchas oportunidades para que las cosas salgan horriblemente mal:

 string s = "The quick brown fox"; int i = Convert.ToInt32(s); 

Como tal, la llamada al método le proporciona documentación para leer, y una amplia sugerencia de que esto es algo más que un simple lanzamiento rápido.

Al diseñar sus propios tipos (particularmente sus propios tipos de valores), puede decidir crear operadores de conversión y funciones de conversión. Las líneas que dividen el “reparto implícito”, el “reparto explícito” y el territorio de la “función de conversión” están un poco borrosas, por lo que diferentes personas pueden tomar decisiones diferentes sobre qué debería ser qué. Solo trate de tener en cuenta la pérdida de información y el potencial de excepciones y datos no válidos, y eso debería ayudarlo a decidir “.

  • Bruce Wood, 16 de noviembre de 2005

http://bytes.com/forum/post1068532-4.html

Casting implica referencias

 List myList = new List(); //up-cast IEnumerable myEnumerable = (IEnumerable) myList; //down-cast List myOtherList = (List) myEnumerable; 

Tenga en cuenta que las operaciones contra myList, como agregar un elemento, se reflejan en myEnumerable y myOtherList. Esto se debe a que todas son referencias (de distintos tipos) a la misma instancia.

Up-casting es seguro. Down-casting puede generar errores de tiempo de ejecución si el progtwigdor ha cometido un error en el tipo. El descifrado seguro está más allá del scope de esta respuesta.

La conversión implica instancias

 List myList = new List(); int[] myArray = myList.ToArray(); 

myList se usa para producir myArray. Esta es una conversión no destructiva (myList funciona perfectamente bien después de esta operación). También observe que las operaciones contra myList, como agregar un elemento, no se reflejan en myArray. Esto se debe a que son instancias completamente separadas.

 decimal w = 1.1m; int x = (int)w; 

Hay operaciones que utilizan la syntax de conversión en C # que en realidad son conversiones .

Dejando la semántica, una prueba rápida muestra que NO son equivalentes.
Hacen la tarea de manera diferente (o tal vez, hacen tareas diferentes).

 x=-2.5 (int)x=-2 Convert.ToInt32(x)=-2 x=-1.5 (int)x=-1 Convert.ToInt32(x)=-2 x=-0.5 (int)x= 0 Convert.ToInt32(x)= 0 x= 0.5 (int)x= 0 Convert.ToInt32(x)= 0 x= 1.5 (int)x= 1 Convert.ToInt32(x)= 2 x= 2.5 (int)x= 2 Convert.ToInt32(x)= 2 

Observe que x=-1.5 y x=1.5 casos.

Un elenco le dice al comstackdor / interperter que el objeto de hecho es de ese tipo (o tiene un tipo de línea / interfaz de ese tipo). Es algo bastante rápido de hacer en comparación con una conversión en la que ya no es el comstackdor / interpertador que hace el trabajo, sino una función que actualiza el análisis de una cadena y hace cálculos matemáticos para convertirla en un número.

Casting siempre significa cambiar el tipo de datos de un objeto. Esto puede hacerse, por ejemplo, convirtiendo un valor flotante en un valor entero o reinterpretando los bits. Usualmente es una operación compatible con el lenguaje (lectura: comstackdor).

El término “conversión” a veces se usa para el casting, pero generalmente lo hace una biblioteca o su propio código y no necesariamente da como resultado el casting. Por ejemplo, si tiene un valor de peso imperial y lo convierte en peso métrico, puede permanecer el mismo tipo de datos (por ejemplo, flotar), pero convertirse en un número diferente. Otro ejemplo típico es la conversión de grados a radianes.

En una forma de hablar agnóstica de lenguaje / marco, la conversión de un tipo o clase a otro se conoce como conversión . Esto también es cierto para .NET, como lo muestran sus primeras cuatro líneas:

 object x; int y; x = 4; y = ( int )x; 

Los lenguajes C y C (como C #) usan la (newtype)somevar para el moldeado. En VB.NET, por ejemplo, hay funciones incorporadas explícitas para esto. La última línea se escribiría como:

 y = CInt(x) 

O, para tipos más complejos:

 y = CType(x, newtype) 

Donde ‘C’ obviamente es la abreviatura de ‘lanzar’.

.NET también tiene la función Convert() , sin embargo. Esta no es una función de lenguaje incorporado (a diferencia de los dos anteriores), sino más bien uno de los marcos. Esto se vuelve más claro cuando utilizas un lenguaje que no necesariamente se usa junto con .NET: es muy probable que tengan sus propios medios de conversión, pero es .NET el que agrega Convert() .

Como dice Matt, la diferencia en el comportamiento es que Convert() es más explícito. En lugar de limitarse a decirle al comstackdor que trate a y como un entero equivalente a x , le está diciendo específicamente que modifique x de una manera adecuada para la clase entera, luego asigne el resultado a y .

En su caso particular, el casting hace lo que se llama ‘unboxing’, mientras que Convert() realmente obtendrá el valor entero. El resultado será el mismo, pero hay diferencias sutiles mejor explicadas por Keith .

De acuerdo con la Tabla 1-7 titulada “Métodos para la conversión explícita” en la página 55 en el Capítulo 1, Lección 4 del MCTS Paced Training Kit (Examen 70-536): Microsoft® .NET Framework 2.0-Application Development Foundation , sin duda hay una diferencia entre ellos.

System.Convert es independiente del lenguaje y convierte “Entre los tipos que implementan la interfaz System.IConvertible “.

El operador de conversión (type) es una característica de lenguaje específico de C # que convierte “Entre tipos que definen operadores de conversión “.

Además, al implementar conversiones personalizadas, los consejos difieren entre ellos.

En la sección titulada Cómo implementar la conversión en tipos personalizados en las páginas 56-57 de la lección citada anteriormente, los operadores de conversión (conversión) están destinados a simplificar las conversiones entre los tipos numéricos, mientras que Convertir () permite las conversiones específicas de la cultura .

La técnica que elijas depende del tipo de conversión que quieras realizar:

  • Defina los operadores de conversión para simplificar el estrechamiento y la ampliación de las conversiones entre los tipos numéricos.

  • Implemente System.IConvertible para habilitar la conversión a través de System.Convert. Utilice esta técnica para habilitar las conversiones específicas de la cultura.

Debería ser más claro ahora que dado que el operador de conversión de conversión se implementa por separado de la interfaz IConvertible, ese Convert () no es necesariamente un nombre más para el casting. (Pero puedo imaginar dónde una implementación puede referirse a la otra para garantizar la coherencia).

No olvide los otros métodos de conversión y conversión de variables: como, Parse, TryParse, así como la conversión implícita entre tipos de datos compatibles.

Este sitio tiene una buena muestra de cuáles son los resultados de la mayoría de los métodos: C # Boxing and Unboxing

Entonces, dadas estas variables de muestra:

 int i = 3, x; long l; string s = "5"; 

Básicamente puedes tener un casting implícito, entre dos tipos compatibles:

 l = i; 

Transmisión explícita usando unboxing o la palabra clave as :

 s = (string)i; //or s = i as string; 

Conversiones explícitas utilizando métodos de System.Convert:

 i = System.Convert.ToInt32(s); 

Conversiones explícitas utilizando métodos de un tipo de datos definido:

 i = int.Parse(s); i = int.TryParse(s, x); 

Conversiones explícitas utilizando métodos de una instancia de una variable:

 s = i.ToString(); 

Creo que el casting es simplemente una forma de hacer asignaciones entre dos tipos compatibles.

La conversión es si necesita copiar explícitamente un valor de un tipo incompatible a otro, y no puede confiar en la coerción malvada .

Algo de buena información también en MSDN: Casting y Type Conversions

Casting es esencialmente solo decirle al tiempo de ejecución que “pretende” que el objeto es el nuevo tipo. En realidad, no convierte ni cambia el objeto de ninguna manera.

Convertir, sin embargo, realizará operaciones para convertir un tipo en otro.

Como ejemplo:

 char caster = '5'; Console.WriteLine((int)caster); 

La salida de esas declaraciones será 53, porque todo el tiempo de ejecución fue mirar el patrón de bits y tratarlo como un int. Lo que terminas obteniendo es el valor ascii del personaje 5, en lugar del número 5.

Sin embargo, si usa Convert.ToInt32 (caster), obtendrá 5 porque realmente lee la cadena y la modifica correctamente. (Esencialmente, sabe que el valor 53 de ASCII es realmente el valor entero 5).

La diferencia es si la conversión es implícita o explícita. El primero es un elenco, el segundo es una llamada más explícita a una función que se convierte. Probablemente hagan lo mismo de diferentes maneras.