Подтвердить что ты не робот

Почему unboxing требует явного литья в С#?

Бокс - это процесс преобразования типа значения в управляемый объект кучи, который является неявным. Unboxing - это обратный процесс, для которого компилятор требует явного приведения. Поскольку бокс хранит тип данных, почему нельзя использовать unboxing, вместо того, чтобы запрашивать явный листинг?

class BoxUnBox
{
 static void Main()
 {
   int i = 123;      // a value type
   object o = i;     // boxing
   int j = (int)o;   // unboxing - Why is an explicit cast required?
 }
}
4b9b3361

Ответ 1

Ваш вопрос не связан только с операцией unboxing. На самом деле это должно звучать так: "Почему я должен использовать явное преобразование?" Рассмотрим следующий пример:

int i = 123;
long l = i;
int j = (int)l; // OMG why??

Ответ прост, и вы можете найти его в спецификации С# 6.2 Явные преобразования:

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

В приведенном выше примере вы можете потерять информацию, потому что long может содержать значения, которые не соответствуют диапазону int. Но вы никогда не потеряете информацию при назначении int long:

long l = i; // safe

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

object o = i;  // implicit and always safe
o = "Now I have a machinegun ho-ho-ho"; // safe too
int j = o;     // will not succeed if o is string

Аналогия

Объектная переменная похожа на черный ящик, где вы можете положить что угодно - музыкальный компакт-диск, ручку, телефон или банан. Не только вы, но и каждый может что-то там положить. Если последнее, что вы положили в черный ящик утром, было бананом, вы можете вернуться вечером и съесть все, что вы вытащите из черного ящика? Если вы живете в одиночестве, и комната закрыта, и ваша память отличная, и... тогда вы можете. И вы будете удивляться, почему все проверяют содержимое своего ящика перед его употреблением. Но если вы не живете в одиночестве, или комната не закрыта, или вы можете сразу забыть, что вы положили телефон в коробку... Приятного аппетита

Ответ 2

Что делать, если кто-то изменяет содержимое o, чтобы сказать "Hello World". Чтобы быть уверенным, что вы знаете, что вы делаете, компилятор требует, чтобы вы явно вводили значение в коробке.

В принципе, неявное преобразование подразумевает, что любой экземпляр o объекта типа также может быть представлен как экземпляр int, который явно не тот. Рассмотрим, например, следующее:

int i = -1;
long j = i;

Ясно, что ваша переменная i, которая является целым числом, также может считаться long. Вот почему здесь неявное литье. С другой стороны, не каждый long также можно преобразовать в int без потери данных. Таким образом, вам нужно явно указать, чтобы определить: я знаю, что может быть некоторая потеря данных, однако мне все равно.

Ответ 3

Поскольку Int32 является Object, но Object может быть Int32. Компилятор знает, что делать в первом случае, но вы должны сообщить компилятору, что знаете, что вы делаете во втором случае, и гарантируете, что unboxing может быть выполнен.

Отношение наследования является направленным! Родитель отличается от ребенка.

Ответ 4

Любой int преобразуется в объект. Не все объекты могут быть переданы в ints.

Ответ 5

Компилятор не может гарантировать, что внутри вашего объекта. Вот почему вам нужно явно указать в качестве значения, которое вы ожидаете. Для компилятора:

Это опасно

object o = 45;
int j = (int)o;

как это:

object o = "something";
int j = (int)o;

И это невозможно во время компиляции.

Ответ 6

Кастинг может завершиться неудачно во время выполнения, в зависимости от того, что действительно содержит object. Если возможно использование unicit unboxing, вы можете упустить ошибки, поскольку вы, возможно, написали что-то, что подразумевалось по-другому (либо вы, либо вы неправильно поняли код другого пользователя). Компилятор требует, чтобы вы произносили явно, потому что вы действительно должны этого сделать. В противном случае вы можете ошибочно смешивать типы, которые производят код ошибки, зависящий от ошибок. Из-за того, что я вынужден явно бросить вас, вы вынуждены дважды подумать, если что вы делаете правильно.

Ответ 7

Использование Dynamic позволяет избежать трансляции

Не отнимать ничего, что было сказано, но я хотел бы указать, что технически приведение не всегда необходимо для выполнения распаковки. Динамическое ключевое слово позволяет системе автоматически выполнять распаковку и преобразование. Я не рекомендую и не рекомендую использовать динамику, просто указывая на ее поведение.

static void DynamicTest()
{
    int i = 123;      // a value type
    object o = i;     // boxing
    dynamic d = o;    // shift to dynamic
    int j = d;        // unboxing - No cast required
}

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