¿Cuál es la diferencia entre el casting y la conversión?

Los comentarios de Eric Lippert en esta pregunta me han dejado completamente confundido. ¿Cuál es la diferencia entre el casting y la conversión en C #?

Creo que lo que Eric está tratando de decir es:

Casting es un término que describe la syntax (de ahí el significado sintáctico ).

La conversión es un término que describe qué acciones se toman realmente detrás de las escenas (y por lo tanto el significado semántico ).

Una expresión de conversión se usa para convertir explícitamente una expresión a un tipo dado.

Y

Una expresión de conversión de la forma (T) E, donde T es un tipo y E es una expresión unaria, realiza una conversión explícita (§13.2) del valor de E al tipo T.

Parece respaldarlo diciendo que un operador de conversión en la syntax realiza una conversión explícita.

Casting es una forma de decirle al comstackdor que “Object X es realmente Type Y, adelante y trátalo como tal”.

La conversión dice “Sé que el Objeto X no es el Tipo Y, pero existe una forma de crear un Objeto nuevo de X de Tipo Y, adelante, hazlo”.

Me acuerdo de la anécdota contada por Richard Feynman en la que él está asistiendo a una clase de filosofía y el profesor le pregunta: “¿Feynman, usted es físico, en su opinión, es un electrón y un ‘objeto esencial’?” Entonces Feynman pregunta la pregunta aclaratoria “¿es un ladrillo un objeto esencial?” a la clase. Cada estudiante tiene una respuesta diferente a esa pregunta. Dicen que la noción fundamental y abstracta de “brickness” es el objeto esencial. No, un ladrillo específico y único es el objeto esencial. No, las partes del ladrillo que puedes observar empíricamente son el objeto esencial. Y así.

Lo cual, por supuesto, no es para responder su pregunta.

No voy a revisar todas estas docenas de respuestas y debatir con sus autores sobre lo que realmente quise decir. Escribiré un artículo de blog sobre el tema en unas semanas y veremos si eso arroja alguna luz sobre el asunto.

¿Qué tal una analogía, a la Feynman? Deseas hornear un pan de plátano el sábado por la mañana (como hago casi todos los sábados por la mañana). Así que consulta The Joy of Cooking, y dice “bla, bla, bla … En otro cuenco, mezcle los ingredientes secos. .. ”

Claramente hay una relación fuerte entre esa instrucción y tus acciones mañana por la mañana, pero igualmente claramente sería un error combinar la instrucción con la acción . La instrucción consiste en texto. Tiene una ubicación, en una página en particular. Tiene puntuación Si estuvieras en la cocina juntando harina y bicarbonato de sodio, y alguien preguntara “¿cuál es tu puntuación en este momento?”, Probablemente pensarías que era una pregunta extraña. La acción está relacionada con la instrucción, pero las propiedades textuales de la instrucción no son propiedades de la acción.

Un yeso no es una conversión de la misma manera que una receta no es el acto de hornear un pastel. Una receta es un texto que describe una acción, que luego puede realizar. Un operador de transmisión es texto que describe una acción, una conversión, que el tiempo de ejecución puede realizar.

De C # Spec 14.6.6:

Una expresión de conversión se usa para convertir explícitamente una expresión a un tipo dado.

Una expresión de conversión de la forma (T) E, donde T es un tipo y E es una expresión unaria, realiza una conversión explícita (§13.2) del valor de E al tipo T.

Así que el casting es una construcción sintáctica utilizada para instruir al comstackdor para que invoque conversiones explícitas.

Del C # Spec §13:

Una conversión permite que una expresión de un tipo sea tratada como otro tipo. Las conversiones pueden ser implícitas o explícitas, y esto determina si se requiere un lanzamiento explícito. [Ejemplo: por ejemplo, la conversión de tipo int a tipo long está implícita, por lo que las expresiones de tipo int se pueden tratar implícitamente como tipo long. La conversión opuesta, de tipo largo a tipo int, es explícita, por lo que se requiere un molde explícito.

Entonces las conversiones son donde se realiza el trabajo real. Observará que la cita de expresión de conversión indica que realiza conversiones explícitas, pero las conversiones explícitas son un superconjunto de conversiones implícitas, por lo que también puede invocar conversiones implícitas (aunque no sea necesario) a través de expresiones de conversión.

Solo mi comprensión, probablemente demasiado simple:

Al transmitir los datos esenciales permanece intacto (la misma representación interna) – “Sé que es un diccionario, pero puede usarlo como una colección”.

Al convertir, está cambiando la representación interna a otra cosa: “Quiero que esta int sea una cadena”.

Después de leer los comentarios de Eric, un bash en inglés sencillo:

Casting significa que los dos tipos son realmente iguales en algún nivel. Pueden implementar la misma interfaz o heredar de la misma clase base o el objective puede ser “lo suficientemente similar” (¿un superconjunto?) Para que funcione el reparto, como el lanzamiento de Int16 a Int32.

La conversión de tipos significa que los dos objetos pueden ser lo suficientemente similares para convertirse. Tome por ejemplo una representación de cadena de un número. Es una cadena, no se puede simplemente convertir en un número, necesita analizarse y convertirse de una a la otra, y el proceso puede fallar. También puede fallar el lanzamiento, pero me imagino que es una falla mucho menos costosa.

Y esa es la diferencia clave entre los dos conceptos, creo. La conversión implicará algún tipo de análisis sintáctico, o un análisis más profundo y la conversión de los datos fuente. Casting no analiza. Simplemente intenta una coincidencia a algún nivel polimórfico.

Casting es la creación de un valor de un tipo a partir de otro valor de otro tipo. La conversión es un tipo de conversión en el que también se debe cambiar la representación interna del valor (en lugar de solo su interpretación).

En C #, el casting y la conversión se realizan con una expresión de conversión:

( tipo ) expresión unaria

La distinción es importante (y el punto se hace en el comentario) porque solo las conversiones pueden ser creadas por un convertidor-operador-declarador . Por lo tanto, solo se pueden crear conversiones (implícitas o explícitas) en el código.

Siempre hay disponible un molde implícito sin conversión para conversiones de subtipo a supertipo, y siempre está disponible un molde explícito sin conversión para conversiones de subtipo a subtipo. No se permiten otros lanzamientos sin conversión.

En este contexto, la conversión significa que está exponiendo un objeto de un tipo dado para su manipulación como otro tipo, la conversión significa que realmente está cambiando un objeto de un tipo dado a un objeto de otro tipo.

Esta página de la documentación de MSDN C # sugiere que un elenco es una instancia específica de conversión: la “conversión explícita”. Es decir, una conversión de la forma x = (int)y es un molde.

Los cambios automáticos del tipo de datos (como myLong = myInt ) son la “conversión” más genérica.

Un elenco es un operador en una clase / estructura. Una conversión es un método / proceso en una u otra de las clases / estructuras afectadas, o puede estar en una clase / estructura completamente diferente (es decir, Converter.ToInt32()

Los operadores de elenco vienen en dos sabores: implícito y explícito

Los operadores de conversión implícitos indican que los datos de un tipo (por ejemplo, Int32) siempre se pueden representar como otro tipo (decimal) sin pérdida de datos / precisión .

 int i = 25; decimal d = i; 

Los operadores de conversión explícita indican que los datos de un tipo (decimal) siempre se pueden representar fielmente como otro tipo (int), pero puede haber pérdida de datos / precisión. Por lo tanto, el comstackdor requiere que usted declare explícitamente que es consciente de esto y desea hacerlo de todos modos, mediante el uso de la syntax de conversión explícita:

 decimal d = 25.0001; int i = (int)d; 

La conversión tiene dos tipos que no están necesariamente relacionados de ninguna manera e intenta convertir uno en el otro a través de algún proceso, como el análisis sintáctico. Si todos los algoritmos de conversión conocidos fallan, el proceso puede lanzar una excepción o devolver un valor predeterminado:

 string s = "200"; int i = Converter.ToInt32(s); // set i to 200 by parsing s string s = "two hundred"; int i = Converter.ToInt32(s); // sets i to 0 because the parse fails 

Las referencias de Eric a la conversión sintáctica frente a la conversión simbólica son básicamente una distinción operador vs. metodología.

Un elenco es sintáctico y puede o no implicar una conversión (según el tipo de elenco). Como saben, C ++ permite especificar el tipo de conversión que desea usar.

Castear arriba / abajo de la jerarquía puede o no considerarse una conversión, dependiendo de a quién le preguntes (¡y de qué idioma están hablando!)

Eric (C #) está diciendo que lanzar un tipo diferente siempre implica una conversión, aunque esa conversión puede incluso no cambiar la representación interna de la instancia.

Un C ++ – chico no estará de acuerdo, ya que un static_cast podría no dar como resultado ningún código adicional (¡así que la “conversión” no es realmente real!)

Casting y Conversion son básicamente el mismo concepto en C #, excepto que una conversión se puede hacer usando cualquier método como Object.ToString() . El casting solo se realiza con el operador de casting (T) E , que se describe en otras publicaciones, y puede hacer uso de las conversiones o el boxeo.

¿Qué método de conversión usa? El comstackdor decide basándose en las clases y bibliotecas proporcionadas al comstackdor en tiempo de comstackción. Si existe una conversión implícita, no es necesario que utilice el operador de conversión. Object o = String.Empty . Si solo existen conversiones explícitas, debe usar el operador de conversión. String s = (String) o .

Puede crear operadores de conversión explicit e implicit en sus propias clases. Nota: las conversiones pueden hacer que los datos se vean muy similares o que no se parezcan al tipo original para usted y para mí, pero todo está definido por los métodos de conversión y lo hace legal para el comstackdor.

Casting siempre se refiere al uso del operador de casting. Puedes escribir

 Object o = float.NaN; String s = (String) o; 

Pero si accede a s , por ejemplo Console.WriteLine , recibirá una InvalidCastException tiempo de ejecución. Por lo tanto, el operador de transmisión aún intenta utilizar la conversión en el momento del acceso, pero se conformará con el boxeo durante la asignación.

INSERTED EDIT # 2: ¿no es una miopía hilarantemente inconsistente que, desde que proporcioné esta respuesta, la pregunta se haya marcado como duplicada de una pregunta que pregunta: ” ¿Es el lanzamiento lo mismo que convertir? “. Y las respuestas de “No” son abrumadoramente subidas de tono . Sin embargo, mi respuesta aquí que señala la esencia generadora de por qué los moldes no son lo mismo que la conversión es abrumadoramente downvoted ( sin embargo, tengo un +1 en los comentarios ). Supongo que los lectores tienen dificultades para comprender que los moldes se aplican en la capa de syntax / semántica denotacional y las conversiones se aplican en la capa de semántica operacional. Por ejemplo, un molde de una referencia (o puntero en C / C ++) – refiriéndose a un tipo de datos encuadrado – a otro tipo de datos, no genera (en todos los idiomas y escenarios) una conversión de los datos encuadrados. Por ejemplo, en C float a[1]; int* p = (int*)&a; float a[1]; int* p = (int*)&a; no asegura que *p refiera a datos int .

Un comstackdor comstack desde la semántica de denotación hasta la semántica operacional . La comstackción no es biyectiva, es decir, no se garantiza que no se compile (por ejemplo, Java, LLVM, asm.js o C # bytecode) a ninguna syntax denotacional que se compile con ese bytecode (por ejemplo, Scala, Python, C #, C mediante Emscripten, etc.) Por lo tanto, las dos capas no son lo mismo.

Por lo tanto, lo más obvio es que un ” reparto ” y una ” conversión ” no son lo mismo. Mi respuesta aquí es señalar que los términos se aplican a dos capas diferentes de semántica. Los lanzamientos se aplican a la semántica de lo que conoce la capa de denotación (syntax de entrada del comstackdor). Las conversiones se aplican a la semántica de lo que conoce la capa operativa (tiempo de ejecución o byte intermedio). Usé el término estándar de ‘borrado’ para describir lo que sucede con la semántica de denotación que no se registra explícitamente en la capa de semántica operacional.

Por ejemplo, los generics reificados son un ejemplo de registro semántico de denotación en la capa de semántica operacional, pero tienen la desventaja de hacer que la capa de semántica operacional sea incompatible con la semántica de denotación de orden superior, por ejemplo, fue doloroso considerar la implementación de la escala superior de Scala. generics generics en CLR de C # porque la semántica denotacional de C # para generics estaba codificada en la capa de semántica operacional.

Vamos chicos, dejen de votar a alguien que sabe mucho más que usted. Haga su tarea primero antes de votar.


EDICION INSERTADA: la conversión es una operación que ocurre en la capa de semántica de denotación (donde los tipos se expresan en su semántica completa). Un elenco puede (por ejemplo, conversión explícita) o no (por ejemplo, la conversión ascendente) provocar una conversión en la capa semántica de tiempo de ejecución. Los votos negativos sobre mi respuesta (y la votación ascendente sobre el comentario de Marc Gavin) me indican que la mayoría de la gente no comprende las diferencias entre la semántica denotacional y la semántica operacional (ejecución) . Suspiro.


Declararé la respuesta de Eric Lippert más simple y más generalmente para todos los idiomas, incluido C # .

Un elenco es la syntax, por lo que (como toda syntax) se borra en tiempo de comstackción ; mientras que, una conversión causa alguna acción en tiempo de ejecución .


Esa es una afirmación verdadera para cada lenguaje de computadora que conozco en todo el universo. Tenga en cuenta que la statement anterior no dice que la conversión y las conversiones son mutuamente excluyentes.

Un elenco puede causar una conversión en tiempo de ejecución, pero hay casos en los que no.

La razón por la que tenemos dos palabras distintas, es decir, conversión y conversión , es que necesitamos una forma de describir por separado lo que está sucediendo en la syntax (el operador de transmisión) y en el tiempo de ejecución (conversión, o verificación de tipo y posible conversión).

Es importante que mantengamos esta separación de conceptos, porque en algunos lenguajes de progtwigción el molde nunca causa una conversión. También para que comprendamos que el casting implícito (p . Ej., Upcasting ) está sucediendo solo en tiempo de comstackción. La razón por la que escribí esta respuesta es porque quiero ayudar a los lectores a entender en términos de ser multilingüe con los lenguajes de computadora. Y también para ver cómo esa definición general también se aplica correctamente en el caso de C #.

También quería ayudar a los lectores a ver cómo generalizo conceptos en mi mente, lo que me ayuda como diseñador de lenguaje de computadora. Estoy tratando de transmitir el don de una forma de pensar muy reduccionista y abstracta. Pero también estoy tratando de explicar esto de una manera muy práctica. Por favor, no dude en dejarme saber en los comentarios si necesito mejorar la elucidación.


Eric Lippert escribió:

Un yeso no es una conversión de la misma manera que una receta no es el acto de hornear un pastel. Una receta es un texto que describe una acción, que luego puede realizar. Un operador de transmisión es texto que describe una acción, una conversión, que el tiempo de ejecución puede realizar.

La receta es lo que está sucediendo en syntax. La syntax siempre se borra, y se reemplaza con nada o algún código de tiempo de ejecución.

Por ejemplo, puedo escribir un elenco en C # que no hace nada y que se borra por completo en tiempo de comstackción cuando no causa un cambio en los requisitos de almacenamiento o es un upcasting . Podemos ver claramente que un elenco es solo una syntax, que no cambia el código de tiempo de ejecución.

 int x = 1; int y = (int)x; Giraffe g = new Giraffe(); Animal a = (Animal)g; 

Esto se puede usar para fines de documentación (aunque ruidoso), pero es esencial en los idiomas que tienen inferencia de tipo, donde a veces es necesario un molde para decirle al comstackdor qué tipo desea inferir.

Por ejemplo , en Scala a None tiene el tipo Option[Nothing] donde Nothing es el tipo inferior que es el subtipo de todos los tipos posibles (no super- type). Por lo tanto, a veces, cuando se utiliza None, el tipo debe enviarse a un tipo específico, porque Scala solo hace una inferencia de tipo local , por lo tanto, no siempre puede inferir el tipo que pretendía.

 // (None : Option[Int]) casts None to Option[Int] println(Some(7) <*> ((None : Option[Int]) <*> (Some(9) > add))) 

Un elenco podría saber en tiempo de comstackción que requiere una conversión de tipo, por ejemplo int x = (int)1.5 , o podría requerir una verificación de tipo y posible conversión de tipo en tiempo de ejecución, por ejemplo, downcasting . El molde (es decir, la syntax) se borra y se reemplaza con la acción de tiempo de ejecución.

Por lo tanto, podemos ver claramente que igualar todos los moldes con conversión explícita es un error de implicación en la documentación de MSDN . Esa documentación tiene la intención de decir que la conversión explícita requiere un operador de transmisión , pero no debe tratar de implicar que todas las conversiones son conversiones explícitas. Estoy seguro de que Eric Lippert puede aclarar esto cuando escribe el blog que prometió en su respuesta.


AGREGAR : De los comentarios y el chat, puedo ver que hay cierta confusión sobre el significado del término borrado .

El término ‘borrado’ se usa para describir información que se conocía en tiempo de comstackción, que no se conoce en tiempo de ejecución. Por ejemplo, los tipos pueden borrarse en generics no reificados, y se llama borrado de tipo .

En general, se borra toda la syntax, ya que generalmente CLI no es biyectivo (invertible y uno a uno) con C #. No siempre puede retroceder desde un código CLI arbitrario al código fuente exacto de C #. Esto significa que la información ha sido borrada.

Aquellos que dicen que borrado no es el término correcto, están combinando la implementación de un elenco con la semántica del elenco. El elenco es una semántica de nivel superior (creo que en realidad es más alta que la syntax , es semántica denotacional, al menos en el caso de upcasting y downcasting) que dice en ese nivel de semántica que queremos lanzar el tipo. Ahora, cómo se hace eso en el tiempo de ejecución es un nivel de semántica completamente diferente. En algunos idiomas, puede ser siempre un NOOP . Por ejemplo, en Haskell toda la información de tipeo se borra en tiempo de comstackción.