C # - Проблемы с боксом / распаковка / типографские вставки. Я не понимаю - PullRequest
8 голосов
/ 07 января 2012

Мне трудно понять это.Рассмотрим следующий пример:

protected void Page_Load(object sender, EventArgs e)
{
    // No surprise that this works
    Int16 firstTest = Convert.ToInt16(0);
    int firstTest2 = (int)firstTest;

    // This also works
    object secondTest = 0;
    int secondTest2 = (int)secondTest;

    // But this fails!
    object thirdTest = Convert.ToInt16(0);
    int thirdtest2 = (int)thirdTest;  // It blows up on this line.
}

Конкретная ошибка, которую я получаю во время выполнения: Specified cast is not valid. Если я QuickWatch (int)thirdTest в Visual Studio, я получаю значение Cannot unbox 'thirdTest' as a 'int'.

Что, черт возьми, здесь происходит?

Ответы [ 3 ]

12 голосов
/ 07 января 2012

Распаковка проверяет точный тип, как описано в документации .

Распаковка - это явное преобразование из объекта типа в тип значения или из типа интерфейса в значениетип, который реализует интерфейс.Операция распаковки состоит из:

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

  • Копированиезначение из экземпляра в переменную типа значения.

Как видите, первым шагом является проверка соответствия экземпляра объекта целевому типу.

Также цитата из документации:

Чтобы распаковка типов значений была успешной во время выполнения, распаковываемый элемент должен быть ссылкой на объект, который был ранее создан путем помещения экземпляра этоготип ценности.Попытка распаковать null вызывает исключение NullReferenceException.Попытка распаковать ссылку на несовместимый тип значения вызывает InvalidCastException.

Поэтому, чтобы исправить эту ошибку, убедитесь, что тип совпадает, прежде чем пытаться распаковать:

object thirdTest = Convert.ToInt16(0);
short thirdtest2 = (short)thirdTest;  
9 голосов
/ 07 января 2012

То, что происходит, это именно то, что говорит.

В первом случае у вас есть короткий, без коробки, который вы затем явно вводите в тип int. Это допустимое преобразование, которое компилятор знает, как это сделать, поэтому оно работает.

Во втором случае у вас есть int, в штучной упаковке, которые присваивают обратно int. Это простая распаковка целого числа, которая также действительна, поэтому она работает.

В третьем случае у вас есть короткое, в штучной упаковке, которое вы пытаетесь распаковать в переменную, которая не является коротким. Это недопустимая операция: вы не можете сделать это за один шаг. Это также не редкая проблема: если вы используете, например, SqlDataReader, который содержит столбец SMALLINT, вы не можете сделать:

    int x = (int)rdr["SmallIntColumn"];

В вашем третьем примере должно работать одно из следующих:

    object thirdTest = Convert.ToInt16(0);
    int thirdTest2 = Convert.ToInt32(thirdTest);
    int thirdTest3 = (int)(short)thirdTest;
4 голосов
/ 07 января 2012

Int16 - модный способ написать short;там не происходит никакого бокса / распаковки, только простое преобразование CLR между 16-битными и 32-битными целыми числами.

Во втором случае блокируются и распаковываются того же типа, что разрешено: тип значения int упаковывается в object, а затем разворачивается.

В третьем случае выполняется попытка распаковать в другой тип (int вместо short), что недопустимо.

...