Вывод типа C # Приводит к получению неправильного типа

StackOverflow https://stackoverflow.com/questions/1506127

  •  19-09-2019
  •  | 
  •  

Вопрос

Я создал следующее свойство, которое выдавало InvalidCastException если к получателю был получен доступ, когда ViewState[TOTAL_RECORD_COUNT] был null.

public long TotalRecordCount
{
    get { return (long)(ViewState[TOTAL_RECORD_COUNT] ?? -1); }
    set { ViewState[TOTAL_RECORD_COUNT] = value; }
}

Моя мысль заключается в том, что он неправильно попытался распаковать объект в ViewState[TOTAL_RECORD_COUNT] к одному int, который потерпел неудачу , потому что он содержал long, но я думаю, что в этой логике может быть изъян.Я оставлю это читателю в качестве упражнения, чтобы указать на этот недостаток.

С тех пор я изменил это свойство на read

public long TotalRecordCount
{
    get { return (long?)ViewState[TOTAL_RECORD_COUNT] ?? -1; }
    set { ViewState[TOTAL_RECORD_COUNT] = value; }
}

который работает просто великолепно.Тем не менее, мне остается гадать, что было не так с моей первоначальной версией...StackOverflow спешит на помощь?

Обратите внимание, что если я попытаюсь выполнить (long)(ViewState[TOTAL_RECORD_COUNT] ?? -1) в немедленном окне я получаю сообщение об ошибке Cannot unbox 'ViewState[TOTAL_RECORD_COUNT] ?? -1' as a 'long' и если я выполню (ViewState[TOTAL_RECORD_COUNT] ?? -1).GetType().Name Я получаю Int32.Я могу казнить (long)-1 и в конечном итоге с -1 в качестве Int64... так в чем дело?

Это было полезно?

Решение

Возвращаемый тип ViewState индексатор - это Object (Я полагаю, вы имеете в виду ASP.NET viewstate здесь).Теперь рассмотрим, что должен сделать компилятор, когда он видит это (что эквивалентно вашему коду).:

object o = ViewState[...];
var x = o ?? -1;

Он должен вывести тип результата выражения o ?? -1 каким-то образом.Слева он видит object, справа находится int.Очевидно, что наиболее общим типом для этого выражения также является object.Однако это означает, что если это действительно заканчивается использованием этого -1 (потому что o было null), ему придется преобразовать его в object - и для int, это означает бокс.

Итак x относится к типу object, и он может содержать int (и, возможно, также какой-то другой интегральный тип - мы не знаем, что находится в вашем viewstate, это может быть short, например).Теперь вы пишете:

long y = (long)x;

С тех пор как x является object, это распаковка коробки.Однако вы можете распаковывать типы значений только в точно такой же тип (с единственным исключением, заключающимся в том, что вы можете заменить подписанный тип эквивалентным неподписанным типом и перечислить его базовый тип).То есть вы не можете распаковать коробку int в long.Гораздо более простым способом воспроизвести это, без "дополнительного" кода, было бы:

object x = 123;
long y = (long)x;

Который также бросает InvalidCastException, и точно по той же причине.

Другие советы

Бросок должен быть всего в один шаг.

Выражение <object> ?? <int> создаст другой объект, и когда первое значение равно null, т.е. ViewState[TOTAL_RECORD_COUNT] равно null, тогда результирующим значением будет объект с вложенным в него значением Int32.

Поскольку вы не можете распаковать объект, содержащий Int32, в long, вам нужно сначала распаковать его в Int32, а затем преобразовать в long.

Проблемы заключаются не в распаковке ViewState[TOTAL_RECORD_COUNT], проблема заключается в упаковке и распаковке -1.

   ViewState[TOTAL_RECORD_COUNT] ?? -1

Вы используете ??оператор для "object" и "int".Результирующий тип - "object".Это означает, что значение -1 будет помещено в рамку (как int), когда поле не существует в состоянии просмотра.

Затем ваша программа завершает работу позже, когда она пытается распаковать (int)-1 как long.

В вашем оригинале, если вы разберете это, вы делали:

(ViewState[TOTAL_RECORD_COUNT] ?? -1)

В оператор объединения нулей (??) специально разработан для:

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

В вашем случае вы используете его для обработки System.Object, поэтому он примет ваше "-1", обработает его как Int32 и поместит в новую систему.Object.Затем он пытается распаковать Int32 в long, что завершается неудачей, поскольку приведение не может распаковать и изменить тип за один шаг.

Вы можете легко решить эту проблему, указав, что ваше значение -1 равно long , используя суффикс L:

public long TotalRecordCount
{
    get { return (long)(ViewState[TOTAL_RECORD_COUNT] ?? -1L); }
    set { ViewState[TOTAL_RECORD_COUNT] = value; }
}

Int64 является типом значения, поэтому приведение null к типу значения всегда будет генерироваться исключение (NullReferenceException).И преобразование Int32 в Int64 будет успешным и не приведет к InvalidCastException.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top