Qual é a diferença entre lançar e coagir?


86

Já vi os dois termos serem usados ​​quase indistintamente em várias explicações online, e a maioria dos livros que consultei também não são totalmente claros sobre a distinção.

Existe uma maneira clara e simples de explicar a diferença que vocês conhecem?

Conversão de tipo (também conhecida como conversão de tipo )

Para usar um valor de um tipo em um contexto que espera outro.

Elenco de tipo não convertível (às vezes conhecido como trocadilho )

Uma mudança que não altera os bits subjacentes.

Coerção

Processo pelo qual um compilador converte automaticamente um valor de um tipo em um valor de outro tipo quando esse segundo tipo é exigido pelo contexto circundante.


Respostas:


115

Conversão de tipo :

A palavra conversão se refere à alteração implícita ou explícita de um valor de um tipo de dados para outro, por exemplo, um inteiro de 16 bits para um inteiro de 32 bits.

A palavra coerção é usada para denotar uma conversão implícita.

A palavra cast geralmente se refere a uma conversão de tipo explícita (em oposição a uma conversão implícita), independentemente de se tratar de uma reinterpretação de um padrão de bits ou uma conversão real.

Portanto, a coerção está implícita, o elenco é explícito e a conversão é qualquer um deles.


Alguns exemplos (da mesma fonte ):

Coerção (implícita):

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

Elenco (explícito):

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

isso tornaria a "coerção implícita" redundante? a nota aqui usa "coerção implícita" e "coerção explícita"
Dave Cousineau

1
A conversão implícita só pode ser feita quando você não está perdendo precisão ou não faz sentido (Ex .: Int -> duplo). Na linguagem mais moderna, você não pode fazer duplo-> int porque você perderia a precisão. Com a coerção de tipo, isso não é um "problema".
Maxime Rouiller de

1
Esta resposta não está de acordo com as especificações definidas na ecma 335 para o CIL. Apresentei a definição da especificação com exemplos em minha resposta.
P.Brian.Mackey

24

Os usos variam, como você observa.

Meus usos pessoais são:

  • Um "elenco" é o uso de um operador de elenco . Um operador de conversão instrui o compilador que (1) essa expressão não é conhecida como do tipo fornecido, mas eu prometo a você que o valor será desse tipo em tempo de execução; o compilador deve tratar a expressão como sendo do tipo dado e o tempo de execução produzirá um erro se não for, ou (2) a expressão é de um tipo totalmente diferente, mas há uma maneira bem conhecida de associar instâncias do tipo da expressão com instâncias do tipo convertido para. O compilador é instruído a gerar o código que executa a conversão. O leitor atento notará que esses são opostos, o que considero um truque interessante.

  • Uma "conversão" é uma operação pela qual um valor de um tipo é tratado como um valor de outro tipo - geralmente um tipo diferente, embora uma "conversão de identidade" ainda seja uma conversão, tecnicamente falando. A conversão pode ser "mudança de representação", como int para double, ou pode ser "preservação de representação" como string para objeto. As conversões podem ser "implícitas", que não exigem uma conversão, ou "explícitas", que exigem uma conversão.

  • Uma "coerção" é uma conversão implícita que altera a representação.


1
Acho que a primeira frase desta resposta é a coisa mais importante de todas. Línguas diferentes usam esses termos para significar coisas bem diferentes. Em Haskell, por exemplo, uma "coerção" nunca muda a representação; uma coerção segura, Data.Coerce.coerce :: Coercible a b => a -> bfunciona para tipos comprovadamente com a mesma representação; Unsafe.Coerce.unsafeCoerce :: a -> bfunciona para quaisquer dois tipos (e fará com que os demônios saiam do seu nariz se você usá-lo da maneira errada).
dfeuer de

@dfeuer ponto de dados interessante, obrigado! Observo que a especificação C # não define "coerção"; minha sugestão é exatamente o que quero dizer. Visto que o termo parece mal definido, geralmente o evito.
Eric Lippert,

9

Casting é o processo pelo qual você trata um tipo de objeto como outro tipo, Coercing é converter um objeto em outro.

Observe que no processo anterior não há conversão envolvida, você tem um tipo que gostaria de tratar como outro, digamos, por exemplo, você tem 3 objetos diferentes que herdam de um tipo base, e você tem um método que tomará esse tipo base, em qualquer ponto, se você conhece o tipo de filho específico, você pode CAST-lo para o que é e usar todos os métodos e propriedades específicas daquele objeto e isso não criará uma nova instância do objeto.

Por outro lado, coagir implica na criação de um novo objeto na memória do novo tipo e então o tipo original seria copiado para o novo, deixando ambos os objetos na memória (até que os coletores de lixo levem um ou ambos) .

Como exemplo, considere o seguinte 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 é passado sem qualquer conversão ou coerção (conversão) porque já é do mesmo tipo baseClass
  • obj2 é implicitamente convertido para base, o que significa que não há criação de um novo objeto porque obj2 já pode ser baseClass
  • obj3 precisa ser convertido de alguma forma para base, você precisará fornecer seu próprio método para converter de otherClasspara baseClass, o que envolverá a criação de um novo objeto do tipo baseClass e preenchê-lo copiando os dados de obj3.

Um bom exemplo é a classe Convert C #, em que fornece código personalizado para converter entre diferentes tipos.


4
Um exemplo ajudaria a esclarecer a distinção que você está tentando fazer.
Oliver Charlesworth

2

Casting preserva o tipo de objetos. A coerção não.

A coerção é pegar o valor de um tipo que NÃO é compatível com a atribuição e converter em um tipo compatível com a atribuição. Aqui eu executo uma coerção porque Int32NÃO herda de Int64... então NÃO é compatível com atribuição. Esta é uma coerção crescente (sem perda de dados). Uma coerção ampliada também é conhecida como conversão implícita . Uma Coerção realiza uma conversão.

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

A conversão permite que você trate um tipo como se fosse de um tipo diferente, ao mesmo tempo que preserva o 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 por James S. Miller

O que é estranho é que a documentação da Microsoft sobre Casting não se alinha com a definição da especificação ecma-335 de Casting.

Conversões explícitas (conversões): as conversões explícitas requerem um operador de conversão. A conversão é necessária quando as informações podem ser perdidas na conversão ou quando a conversão pode não ser bem-sucedida por outros motivos. Os exemplos típicos incluem a conversão numérica para um tipo que tem menos precisão ou um intervalo menor e a conversão de uma instância da classe base em uma classe derivada.

... Isso soa como Coercions, Not Casting.

Por exemplo,

  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.

Quem sabe? Talvez a Microsoft esteja verificando se alguém lê essas coisas.


1

Abaixo está uma postagem do seguinte artigo :

A diferença entre coerção e fundição é freqüentemente negligenciada. Eu posso ver por quê; muitas linguagens têm a mesma sintaxe (ou similar) e terminologia para ambas as operações. Alguns idiomas podem até mesmo se referir a qualquer conversão como “fundição”, mas a explicação a seguir se refere a conceitos no CTS.

Se estiver tentando atribuir um valor de algum tipo a um local de um tipo diferente, você pode gerar um valor do novo tipo que tenha um significado semelhante ao original. Isso é coerção. A coerção permite usar o novo tipo criando um novo valor que de alguma forma se parece com o original. Algumas coerções podem descartar dados (por exemplo, converter o int 0x12345678 para o short 0x5678), enquanto outras não (por exemplo, converter o int 0x00000008 para o short 0x0008 ou o longo 0x0000000000000008).

Lembre-se de que os valores podem ter vários tipos. Se a sua situação for um pouco diferente e você só quiser selecionar um tipo diferente de valor, a fundição é a ferramenta para o trabalho. A conversão simplesmente indica que você deseja operar em um tipo específico que inclui um valor.

A diferença no nível de código varia de C # a IL. Em C #, tanto o elenco quanto a coerção são bastante semelhantes:

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

No nível IL, eles são 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

Quanto ao nível lógico, existem algumas diferenças importantes. O que é mais importante lembrar é que a coerção cria um novo valor, enquanto o casting não. A identidade do valor original e do valor após a conversão são os mesmos, enquanto a identidade de um valor coagido difere do valor original; a coersão cria uma instância nova e distinta, enquanto o elenco não. Um corolário é que o resultado da fundição e o original sempre serão equivalentes (tanto em identidade quanto em igualdade), mas um valor coagido pode ou não ser igual ao original e nunca compartilhar a identidade original.

É fácil ver as implicações da coerção nos exemplos acima, pois os tipos numéricos são sempre copiados por valor. As coisas ficam um pouco mais complicadas quando você está trabalhando com tipos de referência.

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

No exemplo abaixo, uma conversão é um elenco, enquanto a outra é uma coerção.

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

Após essas conversões, tupla e nome são iguais, mas strings não são iguais a nenhum deles. Você poderia tornar a situação um pouco melhor (ou um pouco mais confusa) implementando Equals () e operator == () na classe Name para comparar um Name e uma string []. Esses operadores “consertariam” o problema de comparação, mas você ainda teria duas instâncias separadas; qualquer modificação nas strings não seria refletida no nome ou tupla, enquanto as mudanças em nome ou tupla seriam refletidas no nome e na tupla, mas não nas strings.

Embora o exemplo acima tenha como objetivo ilustrar algumas diferenças entre conversão e coerção, ele também serve como um ótimo exemplo de por que você deve ser extremamente cauteloso ao usar operadores de conversão com tipos de referência em C #.


1

Do padrão CLI :

I.8.3.2 Coerção

Às vezes, é desejável pegar um valor de um tipo que não pode ser atribuído a um local e converter o valor em um tipo que pode ser atribuído ao tipo do local. Isso é realizado por meio da coerção do valor. A coerção pega um valor de um tipo específico e um tipo desejado e tenta criar um valor do tipo desejado que tenha significado equivalente ao valor original. A coerção pode resultar em mudança de representação, bem como mudança de tipo; portanto, a coerção não preserva necessariamente a identidade do objeto.

Existem dois tipos de coerção: alargamento , que nunca perde informações, e estreitamento , em que as informações podem ser perdidas. Um exemplo de uma coerção de alargamento seria a coerção de um valor que é um número inteiro com sinal de 32 bits para um valor que é um número inteiro com sinal de 64 bits. Um exemplo de restrição de coerção é o inverso: forçar um inteiro com sinal de 64 bits em um inteiro com sinal de 32 bits. Linguagens de programação muitas vezes implementam coerções de alargamento como conversões implícitas , enquanto que as coerções de estreitamento geralmente requerem uma conversão explícita .

Alguma coerção é embutida diretamente nas operações VES nos tipos embutidos (consulte §I.12.1). Todas as outras coações devem ser explicitamente solicitadas. Para os tipos integrados, o CTS fornece operações para realizar coerções de alargamento sem verificações de tempo de execução e coerções de estreitamento com verificações de tempo de execução ou truncamento, de acordo com a semântica da operação.

I.8.3.3 Fundição

Como um valor pode ser de mais de um tipo, o uso do valor precisa identificar claramente qual de seus tipos está sendo usado. Como os valores são lidos a partir de locais digitados, o tipo de valor usado é o tipo de local de onde o valor foi lido. Se um tipo diferente for usado, o valor é convertido em um de seus outros tipos. A conversão é geralmente uma operação de tempo de compilação, mas se o compilador não puder saber estaticamente que o valor é do tipo de destino, uma verificação de conversão em tempo de execução será feita. Ao contrário da coerção, um elenco nunca altera o tipo real de um objeto nem altera a representação. Casting preserva a identidade dos objetos.

Por exemplo, uma verificação de tempo de execução pode ser necessária ao lançar um valor lido de um local que é digitado como contendo um valor de uma interface específica. Como uma interface é uma descrição incompleta do valor, a conversão desse valor para um tipo de interface diferente geralmente resultará em uma verificação de conversão em tempo de execução.


1

De acordo com a Wikipedia,

Em ciência da computação, conversão de tipo, conversão de tipo, coerção de tipo e malabarismo de tipo são maneiras diferentes de alterar uma expressão de um tipo de dados para outro.

A diferença entre fundição de tipo e coerção de tipo é a seguinte:

           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 : Casting não é conversão. É apenas o processo pelo qual tratamos um tipo de objeto como outro tipo. Portanto, o tipo real de objeto, bem como a representação, não é alterado durante a fundição.

Concordo com as palavras de @PedroC88:

Por outro lado, coagir implica na criação de um novo objeto na memória do novo tipo e então o tipo original seria copiado para o novo, deixando ambos os objetos na memória (até que os coletores de lixo levem um ou ambos) .

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.