2009-10-01 4 views
2

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

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

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

Я так изменилась, что свойство для чтения

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

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

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

+0

Положите это значение в сеанс! Оставьте ViewState только для пользовательского интерфейса, чувак. – Randolpho

+3

Это может помочь вам понять, что здесь происходит. http://blogs.msdn.com/ericlippert/archive/2009/03/19/representation-and-identity.aspx –

+0

Сессия, кстати, не дружественна к табу. –

ответ

13

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

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

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

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

long y = (long)x; 

С x является object, это распаковка. Тем не менее, вы можете использовать только типы значений типа unbox в одном и том же типе (единственным исключением является то, что вы можете подставить тип подписи для эквивалентного неподписанного типа и перечислить его базовый тип). То есть вы не можете распаковать int в long. Гораздо более простой способ Репрографическое это, без «лишней» коды будет:

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

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

+0

Благодарим вас за четкое и полное объяснение. Сегодня я буду спать намного лучше! –

0

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

+0

Если я что-то не хватает, он не пытается наложить нуль, хотя? На конце он имеет нулевой коалесцирующий оператор. –

+0

Я думаю, что вам нужно немного прочитать код ... фактический откат, который терпит неудачу, пытается сделать -1 для Int64 –

1

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

(ViewState[TOTAL_RECORD_COUNT] ?? -1) 

null-coalescing operator (??) в особенности по предназначено для:

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

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

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

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

. Это не проблема. С помощью ?? с типом значения будет ошибкой типа компиляции. – Daniel

+0

Использование '??' с невообразимым типом значения на левой стороне приведет к ошибке времени компиляции. Кроме того, возвращаемый тип 'ViewState.this []' является 'объектом', который, очевидно, является ссылочным типом. –

1

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

ViewState[TOTAL_RECORD_COUNT] ?? -1 

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

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

5

Литой должен быть только один шаг.

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^