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

85

He visto que ambos términos se usan casi indistintamente en varias explicaciones en línea, y la mayoría de los libros de texto que he consultado tampoco son del todo claras sobre la distinción.

¿Existe quizás una forma clara y sencilla de explicar la diferencia que ustedes conocen?

Conversión de tipos (también conocida como conversión de tipos )

Utilizar un valor de un tipo en un contexto que espera otro.

Reparto de tipo no convertible (a veces conocido como retruécano )

Un cambio que no altera los bits subyacentes.

Coerción

Proceso por el cual un compilador convierte automáticamente un valor de un tipo en un valor de otro tipo cuando ese segundo tipo es requerido por el contexto circundante.

Alexandr Kurilin
fuente
4
¿Qué pasa con el artículo de Wikipedia ?
Oliver Charlesworth

Respuestas:

114

Tipo de conversión :

La palabra conversión se refiere a cambiar implícita o explícitamente un valor de un tipo de datos a otro, por ejemplo, un número entero de 16 bits a un número entero de 32 bits.

La palabra coerción se usa para denotar una conversión implícita.

La palabra fundido normalmente se refiere a una conversión explícita (en contraposición a una conversión implícita), independientemente de si se trata de una reinterpretación de un patrón de bits o una conversión real.

Entonces, la coerción es implícita, el reparto es explícito y la conversión es cualquiera de ellos.


Algunos ejemplos (de la misma fuente ):

Coerción (implícita):

double  d;
int     i;
if (d > i)      d = i;

Reparto (explícito):

double da = 3.3;
double db = 3.3;
double dc = 3.4;
int result = (int)da + (int)db + (int)dc; //result == 9
Igor Oks
fuente
¿Haría esto redundante la "coerción implícita"? la nota aquí usa tanto "coerción implícita" como "coerción explícita"
Dave Cousineau
1
La conversión implícita solo se puede realizar cuando no está perdiendo precisión o no tiene sentido (Ej .: Int -> double). En la mayoría de los lenguajes modernos, no puede hacer double-> int porque perdería precisión. Con la coerción de tipo, eso no es un "problema".
Maxime Rouiller
Esta respuesta no se alinea con las especificaciones definidas en ecma 335 para el CIL. Presenté la definición de la especificación con ejemplos en mi respuesta.
P.Brian.Mackey
24

Los usos varían, como nota.

Mis usos personales son:

  • Un "reparto" es el uso de un operador de reparto . Un operador de conversión le indica al compilador que (1) no se sabe que esta expresión sea del tipo dado, pero le prometo que el valor será de ese tipo en tiempo de ejecución; el compilador debe tratar la expresión como si fuera del tipo dado, y el tiempo de ejecución producirá un error si no lo es, o (2) la expresión es de un tipo completamente diferente, pero hay una forma bien conocida de asociar instancias del tipo de expresión con instancias del tipo de conversión. Se indica al compilador que genere código que realice la conversión. El lector atento notará que estos son opuestos, lo que creo que es un buen truco.

  • Una "conversión" es una operación mediante la cual un valor de un tipo se trata como un valor de otro tipo, generalmente un tipo diferente, aunque una "conversión de identidad" sigue siendo una conversión, técnicamente hablando. La conversión puede ser "cambio de representación", como int a double, o puede ser "preservación de representación" como cadena a objeto. Las conversiones pueden ser "implícitas", que no requieren una conversión, o "explícitas", que sí requieren una conversión.

  • Una "coerción" es una conversión implícita que cambia de representación.

Eric Lippert
fuente
1
Creo que la primera oración de esta respuesta es la más importante de todas. Los diferentes idiomas usan estos términos para significar cosas bastante diferentes. En Haskell, por ejemplo, una "coerción" nunca cambia de representación; una coerción segura, Data.Coerce.coerce :: Coercible a b => a -> bfunciona para tipos que se ha demostrado que tienen la misma representación; Unsafe.Coerce.unsafeCoerce :: a -> bfunciona para dos tipos cualesquiera (y hará que los demonios salgan de su nariz si lo usa mal).
dfeuer
@dfeuer punto de datos interesante, gracias! Observo que la especificación C # no define "coerción"; mi sugerencia es exactamente lo que quiero decir personalmente. Dado que el término parece estar mal definido, generalmente lo evito.
Eric Lippert
8

La conversión es el proceso mediante el cual se trata un tipo de objeto como otro tipo, la coerción es convertir un objeto en otro.

Tenga en cuenta que en el proceso anterior no hay conversión involucrada, tiene un tipo que le gustaría tratar como otro, digamos, por ejemplo, tiene 3 objetos diferentes que heredan de un tipo base, y tiene un método que tomará ese tipo base, en cualquier momento, si conoce el tipo secundario específico, puede CAST lo que es y usar todos los métodos y propiedades específicos de ese objeto y eso no creará una nueva instancia del objeto.

Por otro lado, la coerción implica la creación de un nuevo objeto en la memoria del nuevo tipo y luego el tipo original se copiaría al nuevo, dejando ambos objetos en la memoria (hasta que los recolectores de basura eliminen uno o ambos) .

Como ejemplo, considere el siguiente código:

class baseClass {}
class childClass : baseClass {}
class otherClass {}

public void doSomethingWithBase(baseClass item) {}

public void mainMethod()
{
    var obj1 = new baseClass();
    var obj2 = new childClass();
    var obj3 = new otherClass();

    doSomethingWithBase(obj1); //not a problem, obj1 is already of type baseClass
    doSomethingWithBase(obj2); //not a problem, obj2 is implicitly casted to baseClass
    doSomethingWithBase(obj3); //won't compile without additional code
}
  • obj1 se pasa sin ningún tipo de conversión o coerción (conversión) porque ya es del mismo tipo baseClass
  • obj2 se convierte implícitamente en la base, lo que significa que no hay creación de un nuevo objeto porque obj2 ya puede ser baseClass
  • obj3 debe convertirse de alguna manera a base, deberá proporcionar su propio método para convertir de otherClassa baseClass, lo que implicará crear un nuevo objeto de tipo baseClass y completarlo copiando los datos de obj3.

Un buen ejemplo es la clase Convert C #, donde proporciona código personalizado para convertir entre diferentes tipos.

PedroC88
fuente
3
Un ejemplo ayudaría a aclarar la distinción que está tratando de hacer.
Oliver Charlesworth
2

La fundición conserva el tipo de objetos. La coerción no lo hace.

Coerción es tomar el valor de un tipo que NO es compatible con la asignación y convertirlo a un tipo que es compatible con la asignación. Aquí realizo una coacción porque Int32NO hereda de Int64... entonces NO es compatible con la asignación. Esta es una coerción cada vez mayor (no se pierden datos). Una coerción amplia es también conocida como conversión implícita . Una coacción realiza una conversión.

void Main()
{
    System.Int32 a = 100;
    System.Int64 b = a;
    b.GetType();//The type is System.Int64.  
}

El casting le permite tratar un tipo como si fuera de un tipo diferente mientras también conserva el tipo .

    void Main()
    {
        Derived d = new Derived();
        Base bb = d;
        //b.N();//INVALID.  Calls to the type Derived are not possible because bb is of type Base
        bb.GetType();//The type is Derived.  bb is still of type Derived despite not being able to call members of Test
    }

    class Base 
    {
        public void M() {}
    }

    class Derived: Base
    {
        public void N() {}
    }

Fuente: Estándar anotado de Common Language Infrastructure por James S. Miller

Ahora, lo que es extraño es que la documentación de Microsoft sobre Casting no se alinea con la definición de Casting de la especificación ecma-335.

Conversiones explícitas (conversiones): las conversiones explícitas requieren un operador de conversión. La transmisión es necesaria cuando se puede perder información en la conversión o cuando es posible que la conversión no se realice correctamente 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.

... Esto suena como coerción, no a casting.

Por ejemplo,

  object o = 1;
  int i = (int)o;//Explicit conversions require a cast operator
  i.GetType();//The type has been explicitly converted to System.Int32.  Object type is not preserved.  This meets the definition of Coercion not casting.

¿Quién sabe? Tal vez Microsoft esté comprobando si alguien lee estas cosas.

P. Brian Mackey
fuente
1

A continuación se muestra una publicación del siguiente artículo :

A menudo se pasa por alto la diferencia entre coerción y casting. Puedo ver porque; muchos lenguajes tienen la misma (o similar) sintaxis y terminología para ambas operaciones. Algunos idiomas pueden incluso referirse a cualquier conversión como "conversión", pero la siguiente explicación se refiere a conceptos en el CTS.

Si está intentando asignar un valor de algún tipo a una ubicación de un tipo diferente, puede generar un valor del nuevo tipo que tenga un significado similar al original. Esto es coerción. La coerción le permite usar el nuevo tipo creando un nuevo valor que de alguna manera se parece al original. Algunas coacciones pueden descartar datos (por ejemplo, convertir el int 0x12345678 al corto 0x5678), mientras que otras pueden no (por ejemplo, convertir el int 0x00000008 al corto 0x0008, o el largo 0x0000000000000008).

Recuerde que los valores pueden tener varios tipos. Si su situación es ligeramente diferente y solo desea seleccionar uno diferente de los tipos de valor, el casting es la herramienta para el trabajo. La transmisión simplemente indica que desea operar en un tipo particular que incluye un valor.

La diferencia a nivel de código varía de C # a IL. En C #, tanto la conversión como la coerción se ven bastante similares:

static void ChangeTypes(int number, System.IO.Stream stream)
{
    long longNumber = number;
    short shortNumber = (short)number;

    IDisposable disposableStream = stream;
    System.IO.FileStream fileStream = (System.IO.FileStream)stream;
}

A nivel de IL son bastante diferentes:

ldarg.0
 conv.i8
 stloc.0

ldarg.0
 conv.i2
 stloc.1


ldarg.1
 stloc.2

ldarg.1
 castclass [mscorlib]System.IO.FileStream
 stloc.3

En cuanto al nivel lógico, existen algunas diferencias importantes. Lo más importante a recordar es que la coerción crea un nuevo valor, mientras que el casting no lo hace. La identidad del valor original y el valor después del lanzamiento son los mismos, mientras que la identidad de un valor coaccionado difiere del valor original; la coersión crea una instancia nueva y distinta, mientras que la conversión no lo hace. Un corolario es que el resultado del casting y el original siempre serán equivalentes (tanto en identidad como en igualdad), pero un valor coaccionado puede o no ser igual al original, y nunca comparte la identidad original.

Es fácil ver las implicaciones de la coerción en los ejemplos anteriores, ya que los tipos numéricos siempre se copian por valor. Las cosas se complican un poco cuando se trabaja con tipos de referencia.

class Name : Tuple<string, string>
{
    public Name(string first, string last)
        : base(first, last)
    {
    }

    public static implicit operator string[](Name name)
    {
        return new string[] { name.Item1, name.Item2 };
    }
}

En el siguiente ejemplo, una conversión es un reparto, mientras que la otra es una coerción.

Tuple<string, string> tuple = name;
string[] strings = name;

Después de estas conversiones, la tupla y el nombre son iguales, pero las cadenas no son iguales a ninguno de ellos. Puede hacer que la situación sea un poco mejor (o un poco más confusa) implementando Equals () y operator == () en la clase Name para comparar un Name y una cadena []. Estos operadores "arreglarían" el problema de comparación, pero aún tendría dos instancias separadas; cualquier modificación de las cadenas no se reflejaría en el nombre o la tupla, mientras que los cambios en el nombre o la tupla se reflejarían en el nombre y la tupla, pero no en las cadenas.

Aunque el ejemplo anterior estaba destinado a ilustrar algunas diferencias entre conversión y coerción, también sirve como un gran ejemplo de por qué debe ser extremadamente cauteloso al usar operadores de conversión con tipos de referencia en C #.

Prisionero CERO
fuente
1

Desde el estándar CLI :

I.8.3.2 Coacción

A veces es deseable tomar un valor de un tipo que no se puede asignar a una ubicación y convertir el valor en un tipo que se puede asignar al tipo de la ubicación. Esto se logra mediante la coerción. del valor. La coerción toma un valor de un tipo particular y un tipo deseado e intenta crear un valor del tipo deseado que tiene un significado equivalente al valor original. La coerción puede resultar en un cambio de representación así como un cambio de tipo; por tanto, la coerción no preserva necesariamente la identidad del objeto.

Hay dos tipos de coerción: la ampliación , que nunca pierde información, y la reducción , en la que se puede perder información. Un ejemplo de coerción de ampliación sería coaccionar un valor que es un entero con signo de 32 bits a un valor que es un entero con signo de 64 bits. Un ejemplo de coerción de restricción es lo contrario: convertir un entero de 64 bits con signo en un entero de 32 bits con signo. Los lenguajes de programación a menudo implementan coacciones de ampliación como conversiones implícitas , mientras que las coacciones de reducción suelen requerir una conversión explícita .

Alguna coacción está incorporada directamente en las operaciones VES en los tipos incorporados (ver §I.12.1). Todas las demás coacciones se solicitarán explícitamente. Para los tipos integrados, el CTS proporciona operaciones para realizar coacciones de ampliación sin comprobaciones en tiempo de ejecución y coacciones de reducción con comprobaciones en tiempo de ejecución o truncamiento, según la semántica de la operación.

I.8.3.3 Fundición

Dado que un valor puede ser de más de un tipo, el uso del valor debe identificar claramente cuál de sus tipos se está utilizando. Dado que los valores se leen desde ubicaciones que se escriben, el tipo de valor que se utiliza es el tipo de ubicación desde la que se leyó el valor. Si se va a utilizar un tipo diferente, el valor se convierte en uno de sus otros tipos. La conversión suele ser una operación en tiempo de compilación, pero si el compilador no puede saber estáticamente que el valor es del tipo de destino, se realiza una verificación de conversión en tiempo de ejecución. A diferencia de la coerción, un elenco nunca cambia el tipo real de un objeto ni cambia la representación. El casting conserva la identidad de los objetos.

Por ejemplo, es posible que se necesite una verificación de tiempo de ejecución al convertir un valor leído desde una ubicación que se escribe como que contiene un valor de una interfaz en particular. Dado que una interfaz es una descripción incompleta del valor, convertir ese valor para que sea de un tipo de interfaz diferente generalmente resultará en una verificación de conversión en tiempo de ejecución.

naglas
fuente
1

Según Wikipedia,

En informática, la conversión de tipos, la conversión de tipos, la coerción de tipos y el malabarismo de tipos son formas diferentes de cambiar una expresión de un tipo de datos a otro.

La diferencia entre la conversión de tipos y la coerción de tipos es la siguiente:

           TYPE CASTING           |                   TYPE COERCION
                                  |
1. Explicit i.e., done by user    | 1. Implicit i.e., done by the compiler
                                  |
2. Types:                         | 2. Type:
    Static (done at compile time) |     Widening (conversion to higher data 
                                  |     type)
    Dynamic (done at run time)    |     Narrowing (conversion to lower data 
                                  |     type)
                                  |
3. Casting never changes the      | 3. Coercion can result in representation 
   the actual type of object      |    as well as type change.
   nor representation.            |

Nota : el casting no es conversión. Es solo el proceso mediante el cual tratamos un tipo de objeto como otro tipo. Por lo tanto, el tipo real de objeto, así como la representación, no cambia durante el lanzamiento.

Estoy de acuerdo con las palabras de @ PedroC88:

Por otro lado, la coerción implica la creación de un nuevo objeto en la memoria del nuevo tipo y luego el tipo original se copiaría al nuevo, dejando ambos objetos en la memoria (hasta que los recolectores de basura eliminen uno o ambos) .

Palak Jain
fuente