Pergunta

Eu criei a seguinte propriedade, que jogou um InvalidCastException se o getter foi acessado quando ViewState[TOTAL_RECORD_COUNT] foi null.

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

Meu pensamento é que ele tentou incorretamente para unbox o objeto em ViewState[TOTAL_RECORD_COUNT] a um int, que falhou porque continha um long, mas eu acho que pode haver uma falha em que a lógica. Vou deixar isso como um exercício para o leitor a apontar essa falha.

Eu tenho mudado desde que a propriedade para ler

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

que funciona apenas inchar. Ainda assim, eu fiquei me perguntando o que estava errado com a minha versão original ... StackOverflow para o resgate?

Note que, se eu tentar executar (long)(ViewState[TOTAL_RECORD_COUNT] ?? -1) na janela imediata, fico com a Cannot unbox 'ViewState[TOTAL_RECORD_COUNT] ?? -1' as a 'long' mensagem de erro e se eu executar (ViewState[TOTAL_RECORD_COUNT] ?? -1).GetType().Name eu recebo Int32. Posso executar (long)-1 e acabar com -1 como um Int64 ... então qual é?

Foi útil?

Solução

O tipo de retorno do indexador ViewState é Object (presumo que você quer dizer ASP.NET viewstate aqui). Agora considere o que o compilador tem que fazer quando se vê isso (que é equivalente ao seu código):

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

Tem de deduzir o tipo de resultado de o ?? -1 expressão de alguma forma. À esquerda vê uma object, à direita é um int. Claramente, o tipo mais geral para esta expressão é também object. No entanto, isto significa que se ele realmente acaba usando esse -1 (porque o foi nula), ele terá de convertê-lo para object -. E por um int, isso significa boxe

Assim x é do tipo object, e pode conter um int (e talvez também algum outro tipo integral - não sabemos o que está na sua viewstate, poderia ser short, por exemplo). Agora você escreve:

long y = (long)x;

Desde x é object, este é unboxing. No entanto, você só pode tipos de valor Unbox em mesmo tipo exato (com a única exceção é que você pode substituir um tipo assinado para um tipo não assinado equivalente, e enum para seu tipo base subjacente). Ou seja, você não pode int unbox em long. Uma maneira muito mais simples de repro isso, sem código "extra", seria:

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

O que também joga InvalidCastException, e para exatamente a mesma razão.

Outras dicas

Um elenco tem que ser uma etapa única.

O <object> ?? <int> expressão produzirá um outro objecto, e quando o primeiro valor é nula, isto é. ViewState[TOTAL_RECORD_COUNT] é nulo, então o valor resultante será um objeto, com um Int32 boxed nele.

Uma vez que você não pode unbox um objeto contendo um Int32 para um longo, você precisa primeiro unbox-lo para um Int32, e depois lançá-lo para um longo.

Os problemas não é o unboxing do ViewState[TOTAL_RECORD_COUNT], o problema é o boxing e unboxing do -1.

   ViewState[TOTAL_RECORD_COUNT] ?? -1

Você está usando o ?? operador em "objeto" e "int". O tipo resultante é "objecto". Isto significa que a -1 são embalados (como int) quando o campo não existe no estado de exibição.

Em seguida, o programa trava mais tarde, quando ele tenta unbox o (int) -1 como um longo.

Em seu original, se você quebrá-lo para baixo, você estava fazendo:

(ViewState[TOTAL_RECORD_COUNT] ?? -1)

O nula-coalescência operador (??) é specifially projetado para:

para definir um valor padrão para um tipo de valor anulável , bem como tipos de referência .

No seu caso, você está usando-o para lidar com uma System.Object, por isso vai levar o seu "-1", tratá-lo como um Int32, e caixa-lo em um novo System.Object. Em seguida, ele tenta unbox o Int32 em uma longa, que falha, uma vez que o elenco não pode unbox e altere o tipo em uma única etapa.

Você pode resolver isso facilmente, especificando que o seu -1 é um longo usando o sufixo L:

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

Int64 é um tipo de valor, então lançando null para um tipo de valor será sempre lançar uma exceção (NullReferenceException). E lançando um Int32 para Int64 terá sucesso e não irá lançar uma InvalidCastException.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top