Qual è la differenza tra casting e coercing?


86

Ho visto entrambi i termini essere usati quasi in modo intercambiabile in varie spiegazioni online e la maggior parte dei libri di testo che ho consultato non è del tutto chiara sulla distinzione.

C'è forse un modo chiaro e semplice per spiegare la differenza che voi ragazzi conoscete?

Conversione del tipo (a volte noto anche come cast di tipo )

Utilizzare un valore di un tipo in un contesto che se ne aspetta un altro.

Cast di tipo non convertito (a volte noto come gioco di parole )

Un cambiamento che non altera i bit sottostanti.

Coercizione

Processo mediante il quale un compilatore converte automaticamente un valore di un tipo in un valore di un altro tipo quando il secondo tipo è richiesto dal contesto circostante.


Risposte:


115

Tipo di conversione :

La parola conversione si riferisce alla modifica implicita o esplicita di un valore da un tipo di dati a un altro, ad esempio un intero a 16 bit in un intero a 32 bit.

La parola coercizione è usata per denotare una conversione implicita.

La parola cast si riferisce in genere a una conversione di tipo esplicita (al contrario di una conversione implicita), indipendentemente dal fatto che si tratti di una reinterpretazione di un modello di bit o di una conversione reale.

Quindi, la coercizione è implicita, il cast è esplicito e la conversione è uno di questi.


Pochi esempi (dalla stessa fonte ):

Coercizione (implicita):

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

Cast (esplicito):

double da = 3.3;
double db = 3.3;
double dc = 3.4;
int result = (int)da + (int)db + (int)dc; //result == 9

Ciò renderebbe superflua la "coercizione implicita"? la nota qui usa sia "coercizione implicita" che "coercizione esplicita"
Dave Cousineau

1
La conversione implicita può essere eseguita solo quando non si perde precisione o non si ha senso (Es .: Int -> double). Nella maggior parte dei linguaggi moderni, non puoi fare double-> int perché perderesti precisione. Con la coercizione di tipo, questo non è un "problema".
Maxime Rouiller

1
Questa risposta non è in linea con le specifiche definite in ecma 335 per il CIL. Ho esposto la definizione delle specifiche con esempi nella mia risposta.
P.Brian.Mackey

24

Gli usi variano, come noti.

I miei usi personali sono:

  • Un "cast" è l'utilizzo di un operatore cast . Un operatore cast indica al compilatore che (1) questa espressione non è nota per essere del tipo dato, ma ti prometto che il valore sarà di quel tipo in fase di esecuzione; il compilatore deve trattare l'espressione come se fosse del tipo dato e il runtime produrrà un errore se non lo è, o (2) l'espressione è di un tipo completamente diverso, ma esiste un modo ben noto per associare le istanze del tipo di espressione con istanze del tipo cast-to. Al compilatore viene chiesto di generare il codice che esegue la conversione. Il lettore attento noterà che questi sono opposti, il che penso sia un bel trucco.

  • Una "conversione" è un'operazione mediante la quale un valore di un tipo viene trattato come un valore di un altro tipo, di solito un tipo diverso, sebbene una "conversione di identità" sia ancora una conversione, tecnicamente parlando. La conversione può essere "cambio di rappresentazione", come int in double, o "conservazione della rappresentazione" come stringa in oggetto. Le conversioni possono essere "implicite", che non richiedono un cast, o "esplicite", che richiedono un cast.

  • Una "coercizione" è una conversione implicita che cambia la rappresentazione.


1
Penso che la prima frase di questa risposta sia la cosa più importante di tutte. Lingue diverse usano questi termini per indicare cose molto diverse. In Haskell, ad esempio, una "coercizione" non cambia mai la rappresentazione; una coercizione sicura, Data.Coerce.coerce :: Coercible a b => a -> bfunziona per i tipi che hanno dimostrato di avere la stessa rappresentazione; Unsafe.Coerce.unsafeCoerce :: a -> bfunziona per due tipi qualsiasi (e ti farà uscire i demoni dal naso se lo usi male).
dfeuer

@dfeuer interessante punto dati, grazie! Noto che la specifica C # non definisce la "coercizione"; il mio suggerimento è proprio quello che intendo personalmente. Dato che il termine sembra mal definito, generalmente lo evito.
Eric Lippert

9

Il casting è il processo mediante il quale tratti un tipo di oggetto come un altro tipo, Coercing sta convertendo un oggetto in un altro.

Nota che nel primo processo non è coinvolta alcuna conversione, hai un tipo che vorresti trattare come un altro, ad esempio, hai 3 oggetti diversi che ereditano da un tipo di base e hai un metodo che lo prenderà tipo base, in qualsiasi momento, se conosci il tipo figlio specifico, puoi eseguirne il CAST per quello che è e utilizzare tutti i metodi e le proprietà specifici di quell'oggetto e questo non creerà una nuova istanza dell'oggetto.

D'altra parte, la coercizione implica la creazione di un nuovo oggetto in memoria del nuovo tipo e quindi il tipo originale verrebbe copiato su quello nuovo, lasciando entrambi gli oggetti in memoria (fino a quando i Garbage Collector non portano via uno o entrambi) .

Come esempio si consideri il codice seguente:

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 viene passato senza casting o coercing (conversione) perché è già dello stesso tipo baseClass
  • obj2 è implicitamente castato alla base, il che significa che non c'è creazione di un nuovo oggetto perché obj2 può già esserlo baseClass
  • obj3 deve essere convertito in qualche modo in base, dovrai fornire il tuo metodo per convertire da otherClassa baseClass, il che comporterà la creazione di un nuovo oggetto di tipo baseClass e il suo riempimento copiando i dati da obj3.

Un buon esempio è la classe Convert C # in cui fornisce codice personalizzato per la conversione tra diversi tipi.


4
Un esempio aiuterebbe a chiarire la distinzione che stai cercando di fare.
Oliver Charlesworth

2

La fusione preserva il tipo di oggetti. La coercizione no.

La coercizione consiste nel prendere il valore di un tipo che NON è compatibile con l'assegnazione e convertirlo in un tipo compatibile con l'assegnazione. Qui eseguo una coercizione perché Int32NON eredita da Int64... quindi NON è compatibile con l'assegnazione. Questa è una coercizione crescente (nessun dato perso). Una coercizione che si allarga è alias una conversione implicita . Una coercizione esegue una conversione.

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

La fusione consente di trattare un tipo come se fosse di un tipo diverso preservandone anche il 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() {}
    }

Fonte: The Common Language Infrastructure Annotated Standard di James S. Miller

La cosa strana è che la documentazione di Microsoft su Casting non è in linea con la definizione della specifica ecma-335 di Casting.

Conversioni esplicite (cast): le conversioni esplicite richiedono un operatore cast. Il cast è necessario quando le informazioni potrebbero andare perse durante la conversione o quando la conversione potrebbe non riuscire per altri motivi. Esempi tipici includono la conversione numerica in un tipo con minore precisione o un intervallo inferiore e la conversione di un'istanza della classe base in una classe derivata.

... Suona come Coercions not Casting.

Per esempio,

  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.

Chissà? Forse Microsoft sta controllando se qualcuno legge questa roba.


1

Di seguito è riportato un messaggio dal seguente articolo :

La differenza tra coercizione e casting è spesso trascurata. Posso capire perché; molte lingue hanno la stessa (o simile) sintassi e terminologia per entrambe le operazioni. Alcune lingue possono anche riferirsi a qualsiasi conversione come "casting", ma la seguente spiegazione si riferisce ai concetti nel CTS.

Se stai cercando di assegnare un valore di un tipo a una posizione di un tipo diverso, puoi generare un valore del nuovo tipo che ha un significato simile all'originale. Questa è coercizione. La coercizione ti consente di utilizzare il nuovo tipo creando un nuovo valore che in qualche modo assomigli all'originale. Alcune coercizioni possono scartare i dati (ad esempio convertendo l'int 0x12345678 nello short 0x5678), mentre altre no (ad esempio convertendo l'int 0x00000008 nello short 0x0008, o il lungo 0x0000000000000008).

Ricorda che i valori possono avere più tipi. Se la tua situazione è leggermente diversa e desideri selezionare solo uno diverso dei tipi di valore, il casting è lo strumento per il lavoro. Il casting indica semplicemente che desideri operare su un tipo particolare incluso in un valore.

La differenza a livello di codice varia da C # a IL. In C #, sia il casting che la coercizione sono abbastanza simili:

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 livello IL sono abbastanza diversi:

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

Per quanto riguarda il livello logico, ci sono alcune importanti differenze. La cosa più importante da ricordare è che la coercizione crea un nuovo valore, mentre il casting no. L'identità del valore originale e il valore dopo la fusione sono gli stessi, mentre l'identità di un valore coatto differisce dal valore originale; la coersione crea una nuova istanza distinta, mentre il casting no. Un corollario è che il risultato del casting e l'originale saranno sempre equivalenti (sia nell'identità che nell'uguaglianza), ma un valore coercitivo può o non può essere uguale all'originale e non condivide mai l'identità originale.

È facile vedere le implicazioni della coercizione negli esempi precedenti, poiché i tipi numerici vengono sempre copiati per valore. Le cose diventano un po 'più complicate quando lavori con i tipi di riferimento.

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 };
    }
}

Nell'esempio seguente, una conversione è un cast, mentre l'altra è una coercizione.

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

Dopo queste conversioni, tupla e nome sono uguali, ma le stringhe non sono uguali a nessuna delle due. Si potrebbe rendere la situazione leggermente migliore (o leggermente più confusa) implementando Equals () e l'operatore == () sulla classe Name per confrontare un nome e una stringa []. Questi operatori avrebbero "risolto" il problema di confronto, ma avresti comunque due istanze separate; qualsiasi modifica alle stringhe non si rifletterà nel nome o nella tupla, mentre le modifiche a una di nome o alla tupla si rifletteranno in nome e tupla, ma non nelle stringhe.

Sebbene l'esempio precedente avesse lo scopo di illustrare alcune differenze tra casting e coercizione, serve anche come un ottimo esempio del motivo per cui dovresti essere estremamente cauto nell'usare operatori di conversione con tipi di riferimento in C #.


1

Dallo standard CLI :

I.8.3.2 Coercizione

A volte è consigliabile prendere un valore di un tipo che non è assegnabile a una posizione e convertire il valore in un tipo assegnabile al tipo di posizione. Ciò si ottiene mediante la coercizione del valore. La coercizione prende un valore di un tipo particolare e un tipo desiderato e tenta di creare un valore del tipo desiderato che abbia un significato equivalente al valore originale. La coercizione può comportare un cambio di rappresentazione così come un cambio di tipo; quindi la coercizione non preserva necessariamente l'identità dell'oggetto.

Esistono due tipi di coercizione: allargamento , che non perde mai informazioni, e restringimento , in cui l'informazione potrebbe andare persa. Un esempio di una coercizione di ampliamento potrebbe essere la forzatura di un valore che è un intero con segno a 32 bit a un valore che è un intero con segno a 64 bit. Un esempio di coercizione di restringimento è il contrario: forzare un intero con segno a 64 bit a un intero con segno a 32 bit. I linguaggi di programmazione spesso implementano le coercizioni di ampliamento come conversioni implicite , mentre la riduzione delle coercizioni di solito richiede una conversione esplicita .

Parte della coercizione è incorporata direttamente nelle operazioni VES sui tipi incorporati (vedere §I.12.1). Ogni altra coercizione deve essere esplicitamente richiesta. Per i tipi incorporati, CTS fornisce operazioni per eseguire coercizioni di ampliamento senza controlli di runtime e coercizioni di restringimento con controlli di runtime o troncamento, in base alla semantica dell'operazione.

I.8.3.3 Casting

Poiché un valore può essere di più di un tipo, un utilizzo del valore deve identificare chiaramente quale dei suoi tipi viene utilizzato. Poiché i valori vengono letti da posizioni digitate, il tipo di valore utilizzato è il tipo di posizione da cui è stato letto il valore. Se deve essere utilizzato un tipo diverso, il valore viene convertito in uno dei suoi altri tipi. Il cast è in genere un'operazione in fase di compilazione, ma se il compilatore non può sapere staticamente che il valore è del tipo di destinazione, viene eseguito un controllo del cast in fase di esecuzione. A differenza della coercizione, un cast non cambia mai il tipo effettivo di un oggetto né cambia la rappresentazione. La fusione preserva l'identità degli oggetti.

Ad esempio, potrebbe essere necessario un controllo di runtime quando si esegue il cast di un valore letto da una posizione che viene digitato come contenente un valore di una particolare interfaccia. Poiché un'interfaccia è una descrizione incompleta del valore, il cast di quel valore per essere di un tipo di interfaccia diverso di solito si tradurrà in un controllo del cast in runtime.


1

Secondo Wikipedia,

In informatica, la conversione del tipo, il casting del tipo, la coercizione del tipo e la manipolazione del tipo sono modi diversi per modificare un'espressione da un tipo di dati a un altro.

La differenza tra il casting del tipo e la coercizione del tipo è la seguente:

           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 : la trasmissione non è una conversione. È solo il processo mediante il quale trattiamo un tipo di oggetto come un altro tipo. Pertanto, il tipo effettivo di oggetto, così come la rappresentazione, non viene modificato durante la fusione.

Sono d'accordo con le parole di @ PedroC88:

D'altra parte, la coercizione implica la creazione di un nuovo oggetto in memoria del nuovo tipo e quindi il tipo originale verrebbe copiato su quello nuovo, lasciando entrambi gli oggetti in memoria (fino a quando i Garbage Collector non portano via uno o entrambi) .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.