remarks example cref c# java c++ casting

example - remarks c#



¿Por qué debería evitarse el lanzamiento? (14)

Algunos tipos de fundición son tan seguros y eficientes que a menudo ni siquiera se consideran fundición en absoluto.

Si transfiere de un tipo derivado a un tipo base, generalmente es bastante barato (a menudo, dependiendo del idioma, la implementación y otros factores, es de costo cero) y es seguro.

Si seleccionas desde un tipo simple como int a un tipo más amplio, como int largo, entonces de nuevo a menudo es bastante barato (generalmente no es mucho más caro que asignarle el mismo tipo de conversión) y de nuevo es seguro.

Otros tipos son más tensos y / o más caros. En la mayoría de los lenguajes, la conversión de un tipo base a un tipo derivado es barata pero tiene un alto riesgo de error grave (en C ++ si estático_cast desde base a derivado será barato, pero si el valor subyacente no es del tipo derivado, el comportamiento no está definido y puede ser muy extraño) o relativamente costoso y con el riesgo de generar una excepción (dynamic_cast en C ++, conversión explícita de base en derivada en C #, y así sucesivamente). El boxeo en Java y C # es otro ejemplo de esto, y un gasto aún mayor (considerando que están cambiando más que solo cómo se tratan los valores subyacentes).

Otros tipos de conversión pueden perder información (un tipo entero largo a un tipo entero corto).

Estos casos de riesgo (ya sea de excepción o un error más grave) y de gastos son todas razones para evitar el lanzamiento.

Una razón más conceptual, pero quizás más importante, es que cada caso de casting es un caso en el que su capacidad de razonar sobre la corrección de su código se ve obstaculizada: cada caso es otro lugar donde algo puede salir mal, y las formas en que puede ir mal agregar a la complejidad de deducir si el sistema como un todo va a salir mal. Incluso si el yeso es probablemente seguro cada vez, probar que esto es una parte extra del razonamiento.

Finalmente, el uso intensivo de moldes puede indicar que no se considera bien el modelo de objetos, ya sea al crearlo, al usarlo o a ambos: alternar frecuentemente entre los mismos tipos es casi siempre una falla al considerar las relaciones entre los tipos usado. Aquí no es tanto que los moldes sean malos, ya que son un signo de algo malo.

En general, evito los tipos de conversión tanto como sea posible ya que tengo la impresión de que es una práctica de codificación deficiente y puede incurrir en una penalización de rendimiento.

Pero si alguien me pide que explique por qué exactamente es así, probablemente los mire como un venado en los faros.

Entonces, ¿por qué / cuándo está echando mal?

¿Es general para java, c #, c ++ o cada entorno de tiempo de ejecución diferente se ocupa de él en sus propios términos?

Específicos para cualquier idioma son bienvenidos, por ejemplo, ¿por qué es malo en c ++?


El casting no es intrínsecamente malo, es solo que a menudo se utiliza mal como un medio para lograr algo que realmente no debería o no debería hacerse de manera más elegante.

Si fuera universalmente malo, los idiomas no lo apoyarían. Al igual que cualquier otra función de idioma, tiene su lugar.

Mi consejo sería centrarse en su idioma principal y comprender todos sus modelos y las mejores prácticas asociadas. Eso debería informar las excursiones a otros idiomas.

Los documentos relevantes de C # están here .

Hay un gran resumen sobre las opciones de C ++ en una pregunta previa de SO aquí .


En el caso de C #, se necesita tener más cuidado durante el lanzamiento debido a los gastos generales de boxeo / desembalaje involucrados al tratar con tipos de valores.


En general, las plantillas (o genéricos) son más seguras que los modelos. En ese sentido, diría que un problema con el casting es la seguridad de tipo. Sin embargo, hay otro problema más sutil asociado especialmente con downcasting: diseño. Desde mi punto de vista al menos, downcasting es un olor a código, una indicación de que algo podría estar mal con mi diseño y debería investigar más a fondo. Por qué es simple: si "entiendes" bien las abstracciones, ¡simplemente no lo necesitas! Buena pregunta por cierto ...

¡Aclamaciones!


Has etiquetado esto con tres idiomas, y las respuestas son realmente muy diferentes entre los tres. La discusión de C ++ más o menos implica una discusión de los moldes de C también, y eso da (más o menos) una cuarta respuesta.

Dado que es el que no mencionó explícitamente, comenzaré con C. Los moldes tienen una cantidad de problemas. Una es que pueden hacer cualquiera de una serie de cosas diferentes. En algunos casos, el elenco no hace más que decirle al compilador (en esencia): "cállate, sé lo que estoy haciendo", es decir, garantiza que incluso cuando hagas una conversión que podría causar problemas, el compilador no te advertirá sobre esos problemas potenciales. Solo por ejemplo, char a=(char)123456; . El resultado exacto de esta implementación definida (depende del tamaño y firma de char ), y excepto en situaciones bastante extrañas, probablemente no sea útil. Los modelos también varían en cuanto a si son algo que ocurre solo en tiempo de compilación (es decir, si le estás diciendo al compilador cómo interpretar / tratar algunos datos) o algo que ocurre en tiempo de ejecución (por ejemplo, una conversión real de doble a largo).

C ++ intenta lidiar con eso al menos en cierta medida agregando un número de "nuevos" operadores de elenco, cada uno de los cuales está restringido a solo un subconjunto de las capacidades de un elenco de C. Esto hace que sea más difícil (por ejemplo) realizar accidentalmente una conversión que realmente no pretendía: si solo desea descartar la constness en un objeto, puede usar const_cast y asegurarse de que lo único que puede afectar es si un objeto es const , volatile o no. Por el contrario, un static_cast no puede afectar si un objeto es const o volatile . En resumen, tiene la mayoría de los mismos tipos de capacidades, pero están categorizados, por lo que un elenco generalmente solo puede hacer un tipo de conversión, donde un solo molde de estilo C puede hacer dos o tres conversiones en una sola operación. La excepción principal es que puede usar un dynamic_cast en lugar de un static_cast en al menos algunos casos y, a pesar de estar escrito como un dynamic_cast , realmente terminará como un static_cast . Por ejemplo, puede usar dynamic_cast para recorrer arriba o abajo de una jerarquía de clases, pero un lanzamiento "arriba" de la jerarquía siempre es seguro, por lo que se puede hacer estáticamente, mientras que un lanzamiento "abajo" de la jerarquía no es necesariamente seguro. está hecho dinámicamente

Java y C # son mucho más similares entre sí. En particular, con ambos de fundición es (¿prácticamente?) Siempre una operación en tiempo de ejecución. En términos de los operadores de C ++, normalmente está más cerca de un dynamic_cast en términos de lo que realmente se hace, es decir, cuando intentas convertir un objeto a algún tipo de objetivo, el compilador inserta una verificación en tiempo de ejecución para ver si esa conversión es permitido, y lanzar una excepción si no es así. Los detalles exactos (p. Ej., El nombre utilizado para la excepción de "colada incorrecta") varían, pero el principio básico sigue siendo prácticamente similar (aunque, si la memoria sirve, Java hace los moldes aplicados a los pocos tipos no de objeto como int mucho más cerca de C moldes - pero estos tipos se usan raramente lo suficiente como para que 1) No lo recuerde con certeza, y 2) incluso si es cierto, de todos modos no importa mucho).

En general, la situación es bastante simple (al menos IMO): un elenco (obviamente suficiente) significa que está convirtiendo algo de un tipo a otro. Cuando / si haces eso, se plantea la pregunta "¿Por qué?" Si realmente quieres que algo sea un tipo particular, ¿por qué no lo defines para empezar? Eso no quiere decir que nunca haya una razón para hacer tal conversión, pero en cualquier momento que ocurra, debería surgir la pregunta de si podría rediseñar el código para que se utilizara el tipo correcto. Incluso las conversiones aparentemente inocuas (por ejemplo, entre enteros y coma flotante) deben examinarse mucho más de cerca de lo que es común. A pesar de su aparente similitud, los enteros realmente deberían usarse para tipos de cosas "contadas" y como puntos flotantes para tipos de cosas "medidas". Ignorar la distinción es lo que lleva a algunas de las locuras declaraciones como "la familia estadounidense promedio tiene 1,8 hijos". Aunque todos podemos ver cómo sucede eso, el hecho es que ninguna familia tiene 1.8 hijos. Podrían tener 1 o podrían 2 o podrían tener más que eso, pero nunca 1.8.


Java, c # y c ++ son lenguajes fuertemente tipados, aunque los lenguajes fuertemente tipados pueden considerarse inflexibles, tienen la ventaja de realizar comprobaciones de tipos en tiempo de compilación y te protegen contra los errores de tiempo de ejecución causados ​​por tener el tipo incorrecto para ciertas operaciones.

Hay básicamente dos tipos de moldes: un yeso para un tipo más general o un yeso para otros tipos (más específico). La conversión a un tipo más general (conversión a un tipo principal) dejará intactas las comprobaciones del tiempo de compilación. Pero la conversión a otros tipos (tipos más específicos) deshabilitará la verificación del tipo de tiempo de compilación y será reemplazado por el compilador mediante una verificación en tiempo de ejecución. Esto significa que tiene menos certeza de que el código compilado se ejecutará correctamente. También tiene un impacto insignificante en el rendimiento, debido a la verificación adicional del tipo de tiempo de ejecución (¡la API de Java está llena de versiones!).


Los errores de transmisión siempre se informan como errores de tiempo de ejecución en java. El uso de genéricos o plantillas convierte estos errores en errores en tiempo de compilación, por lo que es mucho más fácil detectar cuándo se ha cometido un error.

Como dije arriba. Esto no quiere decir que todo el casting sea malo. Pero si es posible evitarlo, es mejor hacerlo.


Los programadores tienden cada vez más a aferrarse a las reglas dogmáticas sobre el uso de las características del lenguaje ("¡Nunca use XXX!", "XXX considerado nocivo", etc.), donde XXX abarca desde goto s a punteros a miembros de datos protected a singletons objetos por valor

Seguir estas reglas, en mi experiencia, garantiza dos cosas: no serás un terrible programador ni serás un gran programador.

Un enfoque mucho mejor es excavar y descubrir el núcleo de la verdad detrás de estas prohibiciones generales, y luego usar las características juiciosamente, en el entendimiento de que hay muchas situaciones para las cuales son la mejor herramienta para el trabajo.

"Generalmente evito los tipos de conversión tanto como sea posible" es un buen ejemplo de una regla sobregeneralizada. Los yesos son esenciales en muchas situaciones comunes. Algunos ejemplos:

  1. Al interoperar con código de terceros (especialmente cuando ese código está plagado de typedef s). (Ejemplo: GLfloat <-> double <-> Real .)
  2. Transmitir desde un puntero / referencia derivado a la clase base: Esto es tan común y natural que el compilador lo hará implícitamente. Si hacerlo explícito aumenta la legibilidad, ¡el lanzamiento es un paso adelante, no al revés!
  3. Transmitir desde una base al puntero / referencia de clase derivado: también es común, incluso en código bien diseñado. (Ejemplo: contenedores heterogéneos)
  4. Dentro de la serialización / deserialización binaria u otro código de bajo nivel que necesita acceso a los bytes sin formato de los tipos incorporados.
  5. En cualquier momento cuando sea simplemente más natural, conveniente y legible utilizar un tipo diferente. (Ejemplo: std::size_type -> int .)

Ciertamente, hay muchas situaciones en las que no es apropiado usar un yeso, y es importante aprenderlas también; No entraré en demasiados detalles ya que las respuestas anteriores han hecho un buen trabajo al señalar algunas de ellas.


Muchas buenas respuestas aquí. Así es como lo veo (desde una perspectiva de C #).

Casting generalmente significa una de dos cosas:

  • Conozco el tipo de tiempo de ejecución de esta expresión, pero el compilador no lo sabe. Compilador, le digo que, en tiempo de ejecución, el objeto que corresponde a esta expresión realmente será de este tipo. A partir de ahora, sabes que esta expresión debe ser tratada como de este tipo. Genero código que asume que el objeto será del tipo dado, o, lanzo una excepción si estoy equivocado.

  • Tanto el compilador como el desarrollador conocen el tipo de tiempo de ejecución de la expresión. Hay otro valor de un tipo diferente asociado con el valor que tendrá esta expresión en tiempo de ejecución. Generar código que produzca el valor del tipo deseado a partir del valor del tipo dado; si no puedes hacerlo, entonces lanza una excepción.

Tenga en cuenta que esos son opuestos . ¡Hay dos tipos de moldes! Hay versiones en las que le está dando una pista al compilador sobre la realidad - hey, esta cosa de tipo objeto es en realidad de tipo Cliente - y hay conversiones en las que le está diciendo al compilador que realice un mapeo de un tipo a otro - hey, Necesito el int que corresponde a este doble.

Ambos tipos de moldes son banderas rojas. El primer tipo de elenco plantea la pregunta "¿por qué exactamente el desarrollador sabe algo que el compilador no sabe?" Si se encuentra en esa situación, lo mejor que puede hacer es cambiar el programa para que el compilador controle la realidad. Entonces no necesitas el yeso; el análisis se realiza en tiempo de compilación.

El segundo tipo de elenco plantea la pregunta "¿por qué no se realiza la operación en el tipo de datos de destino en primer lugar?" Si necesita un resultado en ints, ¿por qué tiene un doble en primer lugar? ¿No deberías tener una int?

Algunos pensamientos adicionales aquí:

http://blogs.msdn.com/b/ericlippert/archive/tags/cast+operator/


No estoy seguro si alguien ya mencionó esto, pero en C # casting se puede usar de una manera bastante segura, y a menudo es necesario. Supongamos que recibe un objeto que puede ser de varios tipos. Con la palabra clave es, primero puede confirmar que el objeto es del tipo que está a punto de convertirlo y luego convertir el objeto directamente a ese tipo. (No trabajé mucho con Java, pero estoy seguro de que también hay una manera muy directa de hacerlo).


Para elaborar sobre la respuesta de KDeveloper , no es intrínsecamente seguro para el tipo. Con el casting, no hay garantía de que lo que estás lanzando y fundiendo coincidirá, y si eso ocurre, obtendrás una excepción de tiempo de ejecución, lo cual siempre es algo malo.

Con respecto específicamente a C #, porque incluye los operadores is y as , tiene la oportunidad (en su mayor parte) de determinar si un elenco tendrá éxito o no. Debido a esto, debe seguir los pasos apropiados para determinar si la operación tendrá éxito o no y proceder de manera adecuada.


Para ser realmente conciso, una buena razón es la portabilidad. Una arquitectura diferente que acomode el mismo idioma podría tener, digamos, tamaños diferentes. Entonces, si migro de ArchA a ArchB, que tiene una int más estrecha, es posible que vea un comportamiento extraño en el mejor de los casos, y fallar segmente en el peor.

(Estoy claramente ignorando bytecode e IL independientes de la arquitectura)


Principalmente estoy hablando de C ++ aquí, pero la mayoría de esto probablemente también se aplica a Java y C #:

C ++ es un lenguaje estáticamente tipado . Hay algunos márgenes que el lenguaje le permite en esto (funciones virtuales, conversiones implícitas), pero básicamente el compilador conoce el tipo de cada objeto en tiempo de compilación. La razón para usar dicho lenguaje es que los errores se pueden capturar en tiempo de compilación . Si el compilador conoce los tipos de a y b , entonces lo capturará en tiempo de compilación cuando haga a=b donde a es un número complejo b es una cadena.

Siempre que hagas un casting explícito, dile al compilador que se calle, porque piensas que lo sabes mejor . En caso de que estés equivocado, generalmente solo lo descubrirás en tiempo de ejecución . Y el problema de descubrir en el tiempo de ejecución es que esto podría ser del cliente.


Solo lanzas un objeto a algún tipo, si se cumplen 2 condiciones:

  1. sabes que es de ese tipo
  2. el compilador no

Esto significa que no toda la información que tiene está bien representada en la estructura de tipos que utiliza. Esto es malo, porque su implementación debe comprender semánticamente su modelo, lo que claramente no sucede en este caso.

Ahora cuando haces un yeso, entonces esto puede tener 2 razones diferentes:

  1. Hiciste un mal trabajo al expresar las relaciones tipo.
  2. el sistema de tipos de idiomas simplemente no es lo suficientemente expresivo para expresarlos.

En la mayoría de los idiomas, te encuentras con la segunda situación muchas veces. Los genéricos como en Java ayudan un poco, el sistema de plantillas de C ++ aún más, pero es difícil de dominar y aun así algunas cosas pueden ser imposibles o simplemente no vale la pena el esfuerzo.

Entonces, podrías decir que un reparto es un truco sucio para eludir tus problemas y expresar alguna relación de tipo específico en un lenguaje específico. Los trucos sucios deberían evitarse. Pero nunca puedes vivir sin ellos.