В чем разница между кастингом и принуждением?


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

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

преобразования типа (также иногда известный как тип cast)

использовать значение одного типа в контексте, который ожидает другой.

неконвертирующий тип cast (иногда известный как каламбур типа)

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

принуждение

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

5 70

5 ответов:

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

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

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

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

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


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

принуждение (неявный):

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

Cast (явный):

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

обычаи меняются, как вы отмечаете.

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

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

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

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

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

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

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

ниже есть сообщение от статье:

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

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

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

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

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 = = () на имя класса, чтобы сравнить имя и строку[]. Эти операторы "исправят" проблему сравнения, но у вас все равно будет два отдельных экземпляра; любая модификация строк не будет отражена в имени или кортеже, в то время как изменения либо одного из имени или Кортежа будут отражены в имени и кортеже, но не в строках.

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

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

принуждение принимает значение типа, не совместимого с назначением, и преобразуется в тип, совместимый с назначением. Здесь я выполняю принуждение, потому что 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 на литье не соответствует спецификации 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 проверяет, читает ли кто-нибудь этот материал.