В чем разница между литьем и принуждением?

Я видел, что оба термина используются почти взаимозаменяемо в различных онлайн-объяснениях, и большинство текстов, которые я консультировал, также не совсем понятны в отношении различия.

Возможно, существует ясный и простой способ объяснить разницу, о которой вы, ребята, знаете?

Преобразование типов (также иногда называемое приведение типов)

Использовать значение одного типа в контексте, ожидающем другого.

Неконвертируемый тип литья (иногда известный как тип pun)

Изменение, которое не изменяет базовые бит.

Принуждение

Процесс, посредством которого компилятор автоматически преобразует значение одного типа в значение другого типа, когда этот второй тип требуется окружающему контексту.

Ответ 1

Преобразование типов:

Слово преобразование означает либо неявно, либо явно меняет значение из одного типа данных в другой, например. 16-разрядное целое число с 32-разрядным целым числом.

Слово принуждение используется для обозначения неявного преобразования.

Слово cast обычно относится к явному преобразованию типа (в отличие от неявного преобразования), независимо от того, является ли это повторной интерпретацией битового шаблона или реального преобразования.

Итак, принуждение является неявным, cast явно, а преобразование - любое из них.


Несколько примеров (из того же источника):

Принуждение (неявное):

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

Листинг (явный):

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

Ответ 2

Использование отличается, как вы заметили.

Мои личные обычаи:

  • "cast" - это использование оператора трансляции. Оператор литья инструктирует компилятор, что либо (1) это выражение не известно, что оно относится к данному типу, но я обещаю вам, что это значение будет во время выполнения; компилятор должен рассматривать выражение как имеющее данный тип, а среда выполнения создает ошибку, если это не так, или (2) выражение имеет совсем другой тип целиком, но существует общеизвестный способ связывания экземпляров типа выражения с экземплярами типа cast-to. Компилятору предписывается генерировать код, который выполняет преобразование. Внимательный читатель заметит, что это противоположности, которые я считаю аккуратным трюком.

  • "Преобразование" - это операция, при которой значение одного типа рассматривается как значение другого типа - обычно другого типа, хотя "преобразование идентичности" по-прежнему является преобразованием, с технической точки зрения. Преобразование может быть "изменением представления", например, int double, или оно может быть "сохранением представления", как строка для объекта. Конверсии могут быть "неявными", для которых не требуется листинг, или "явный", для которого требуется листинг.

  • "Принуждение" - это неявное преобразование, изменяющее представление.

Ответ 3

Кастинг - это процесс, с помощью которого вы обрабатываете тип объекта как другой тип, Coercing преобразует один объект в другой.

Обратите внимание, что в первом процессе нет никакого преобразования, у вас есть тип, который вы хотели бы рассматривать как другой, например, у вас есть 3 разных объекта, которые наследуются от базового типа, и у вас есть метод, который возьмет этот базовый тип в любой момент, если вы теперь являетесь конкретным типом дочернего элемента, вы можете CAST его к тому, что он есть, и использовать все конкретные методы и свойства этого объекта, и это не создаст новый экземпляр объекта.

С другой стороны, принуждение подразумевает создание нового объекта в памяти нового типа, а затем исходный тип будет скопирован на новый, оставив оба объекта в памяти (пока сборщики мусора не уберут, или оба).

Ответ 4

Кастинг сохраняет тип объектов. Принуждения нет.

Принуждение принимает значение типа, который НЕ совместим с присваиванием, и преобразует его в тип, совместимый с присваиванием. Здесь я выполняю приведение, потому что Int32 НЕ наследуется от Int64... поэтому он НЕ совместим по присваиванию. Это расширяющееся принуждение (данные не теряются). Расширение принуждения - это неявное преобразование. Принуждение выполняет преобразование.

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

Приведение позволяет вам обрабатывать тип, как если бы он был другого типа, сохраняя при этом тип.

    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() {}
    }

Источник: Стандарт аннотированной инфраструктуры общего языка Джеймсом Миллером

Теперь странным является то, что документация Microsoft по Casting не соответствует определению Casting в спецификации ecma-335.

Явные преобразования (приведение). Для явных преобразований требуется оператор приведения. Преобразование требуется, когда информация может быть потеряна при преобразовании или когда преобразование может не завершиться успешно по другим причинам. Типичные примеры включают преобразование чисел в тип с меньшей точностью или меньшим диапазоном и преобразование экземпляра базового класса в производный класс.

... Это звучит как Принуждение, а не Кастинг.

Например,

  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.

Кто знает? Возможно, Microsoft проверяет, читает ли кто-нибудь этот материал.

Ответ 5

Ниже приведена публикация следующей статьи:

Различие между принуждением и литьем часто игнорируется. Я понимаю, почему; многие языки имеют одинаковый (или похожий) синтаксис и терминологию для обеих операций. Некоторые языки могут даже ссылаться на любое преобразование как на "литье", но следующее объяснение относится к понятиям в CTS.

Если вы пытаетесь присвоить значение некоторого типа местоположению другого типа, вы можете сгенерировать значение нового типа, имеющего аналогичное значение для оригинала. Это принуждение. Принуждение позволяет использовать новый тип, создавая новое значение, которое каким-то образом напоминает оригинал. Некоторые принуждения могут отбрасывать данные (например, преобразование int 0x12345678 в короткий 0x5678), в то время как другие могут не включать (например, преобразование int 0x00000008 в короткий 0x0008 или длинный 0x0000000000000008).

Вспомните, что значения могут иметь несколько типов. Если ваша ситуация немного отличается, и вы хотите выбрать только один из типов значений, кастинг - это инструмент для задания. Кастинг просто указывает, что вы хотите работать с определенным типом, который включает значение.

Разница на уровне кода варьируется от С# до IL. В С# оба литья и принуждения выглядят довольно схожими:

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

На уровне IL они совершенно разные:

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

Что касается логического уровня, есть некоторые важные отличия. Самое главное помнить, что принуждение создает новое значение, а кастинг - нет. Идентичность исходного значения и значения после кастинга одинаковы, а идентификатор принудительного значения отличается от исходного значения; coersion создает новый, отличный экземпляр, в то время как литье не делает. Следствием является то, что результат кастинга и оригинала всегда будет эквивалентным (как по идентичности, так и по равенству), но принудительное значение может быть или не быть равно оригиналу и никогда не будет делиться оригинальным идентификатором.

Легко видеть последствия принуждения в приведенных выше примерах, поскольку числовые типы всегда копируются по значению. Все становится немного сложнее, если вы работаете со ссылочными типами.

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

В приведенном ниже примере одно преобразование является литой, а другое - принуждением.

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

После этих преобразований кортеж и имя равны, но строки не равны ни одному из них. Вы могли бы сделать ситуацию немного лучше (или немного более запутанной), реализовав Equals() и operator ==() в классе Name, чтобы сравнить имя и строку []. Эти операторы "исправили" проблему сравнения, но у вас все еще было бы два отдельных экземпляра; любая модификация строк не будет отображаться в имени или кортеже, тогда как изменения в имени или кортеже будут отображаться в имени и кортеже, но не в строках.

Хотя приведенный выше пример призван проиллюстрировать некоторые различия между литьем и принуждением, он также служит отличным примером того, почему вы должны быть крайне осторожны в использовании операторов преобразования со ссылочными типами на С#.

Ответ 6

Из стандарта CLI:

I.8.3.2 Принуждение

Иногда желательно взять значение типа, который не может быть назначен для местоположения, и преобразовать значение в тип, который может быть назначен, в тип местоположения. Это достигается путем принуждения ценности. Принуждение принимает значение определенного типа и требуемого типа и пытается создать значение требуемого типа, которое имеет значение, эквивалентное исходному значению. Принуждение может привести к изменению представления, а также к изменению типа; следовательно, принуждение не обязательно сохраняет идентичность объекта.

Существует два вида принуждения: расширение, которое никогда не теряет информацию, и сужение, при котором информация может быть потеряна. Примером принудительного расширения может быть приведение значения, представляющего собой 32-разрядное целое число со знаком, к значению, являющемуся 64-разрядным целым числом со знаком. Примером сужающего приведения является обратный: приведение 64-разрядного целого числа со знаком к 32-разрядному целому числу со знаком. Языки программирования часто реализуют расширяющие приведения как неявные преобразования, тогда как сужающие приведения обычно требуют явного преобразования.

Некоторое принуждение встроено непосредственно в операции VES для встроенных типов (см. §I.12.1). Все другие виды принуждения должны быть явно запрошены. Для встроенных типов CTS предоставляет операции для выполнения расширений при отсутствии проверок во время выполнения и сужений при проверках или усечении во время выполнения в соответствии с семантикой операции.

I.8.3.3 Кастинг

Поскольку значение может иметь более одного типа, использование значения должно четко определять, какой из его типов используется. Поскольку значения считываются из введенных местоположений, тип используемого значения является типом местоположения, из которого было прочитано значение. Если другой тип должен быть использован, значение приводится к одному из других его типов. Приведение обычно является операцией времени компиляции, но если компилятор не может статически знать, что значение относится к целевому типу, выполняется проверка приведения во время выполнения. В отличие от принуждения, приведение никогда не изменяет фактический тип объекта и не изменяет представление. Кастинг сохраняет идентичность предметов.

Например, проверка времени выполнения может потребоваться при приведении значения, считанного из местоположения, которое напечатано как содержащее значение определенного интерфейса. Поскольку интерфейс представляет собой неполное описание значения, приведение этого значения к другому типу интерфейса обычно приводит к проверке приведения во время выполнения.

Ответ 7

Согласно Википедии,

В информатике преобразование типов, приведение типов, приведение типов и жонглирование типов являются различными способами изменения выражения из одного типа данных в другой.

Разница между приведением типов и приведением типов заключается в следующем:

           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.            |

Примечание: кастинг не является конверсией. Это просто процесс, с помощью которого мы рассматриваем тип объекта как другой тип. Следовательно, фактический тип объекта, а также представление не изменяются во время приведения.

Я согласен со словами @PedroC88:

С другой стороны, принуждение подразумевает создание нового объекта в памяти нового типа, а затем исходный тип будет скопирован в новый, оставив оба объекта в памяти (пока сборщики мусора не уберут или не уберут оба),