Domanda

ho creato il seguente proprietà, che ha gettato un InvalidCastException se il getter era accessibile quando ViewState[TOTAL_RECORD_COUNT] era null.

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

Il mio pensiero è che erroneamente tentato di unboxing l'oggetto in ViewState[TOTAL_RECORD_COUNT] a un int, che non è riuscito perché conteneva un long, ma credo che ci potrebbe essere un difetto in quella logica. Lascio come esercizio per il lettore a sottolineare che difetto.

Da allora ho cambiato proprietà per leggere

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

che funziona esattamente gonfiarsi. Eppure, io sono rimasto chiedo che cosa era sbagliato con la mia versione originale ... StackOverflow in soccorso?

Si noti che se provo ad eseguire (long)(ViewState[TOTAL_RECORD_COUNT] ?? -1) nella finestra immediata, ottengo il messaggio di errore Cannot unbox 'ViewState[TOTAL_RECORD_COUNT] ?? -1' as a 'long' e se eseguo (ViewState[TOTAL_RECORD_COUNT] ?? -1).GetType().Name ricevo Int32. Posso eseguire (long)-1 e finire con -1 come Int64 ... quindi cosa succede?

È stato utile?

Soluzione

Il tipo di ritorno di ViewState indicizzatore è Object (presumo si intende ASP.NET viewstate qui). Consideriamo ora ciò che il compilatore ha a che fare quando si vede questo (che equivale al codice):

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

Si deve dedurre il tipo di risultato di espressione o ?? -1 in qualche modo. Sulla sinistra si vede un object, sulla destra è un int. Chiaramente, il tipo più generale di questa espressione è anche object. Tuttavia, questo significa che se finisce in realtà per usare che -1 (perché o era nullo), dovrà convertirlo in object -. E per un int, questo significa che la boxe

Quindi x è di tipo object, e può contenere un int (e forse anche qualche altro tipo integrale - non sappiamo che cosa è nel vostro ViewState, potrebbe essere short, per esempio). Ora si scrive:

long y = (long)x;

Dal x è object, questo è unboxing. Tuttavia, è possibile solo tipi di valore Unbox in esatta stesso tipo (con la sola eccezione che è possibile sostituire un tipo firmato per un tipo senza segno equivalente, e enum per il suo tipo di base sottostante). Cioè, non si può int Unbox in long. Un modo molto più semplice a Repro questo, senza codice "extra", potrebbe essere:

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

che getta anche InvalidCastException, e per esattamente lo stesso motivo.

Altri suggerimenti

Un cast deve essere solo un passo.

Il <object> ?? <int> espressione produrrà un altro oggetto, e quando il primo valore è nullo, cioè. ViewState[TOTAL_RECORD_COUNT] è nullo, allora il valore risultante sarà un oggetto, con un Int32 scatolato in esso.

Poiché non è possibile unboxing un oggetto contenente un Int32 a un lungo, è necessario prima di unboxing ad un Int32, e poi gettato in un lungo.

Il problema non è l'unboxing del ViewState[TOTAL_RECORD_COUNT], il problema è la boxe e unboxing del -1.

   ViewState[TOTAL_RECORD_COUNT] ?? -1

Si utilizza la ?? operatore "oggetto" e "int". Il tipo risultante è "oggetto". Ciò significa che l'-1 verrà imballata (come int) quando il campo non esiste nello stato di visualizzazione.

Poi il programma si blocca più tardi, quando si tenta di unboxing del (int) -1 come un lungo.

Nel vostro originale, se si scomposizione, si stava facendo:

(ViewState[TOTAL_RECORD_COUNT] ?? -1)

Il null coalescenza operatore (??) è specifially progettato per:

  

per definire un valore predefinito per tipi valore Null così come tipi di riferimento.

Nel tuo caso, lo si usa per gestire una System.Object, quindi sta andando a prendere il vostro "-1", trattarlo come un Int32, e la scatola in un nuovo System.Object. Poi, tenta di unboxing del Int32 in un lungo, che non riesce, dal momento che il cast non può unboxing e cambiare il tipo in un unico passaggio.

È possibile risolvere questo facilmente specificando che la vostra -1 è un lungo utilizzando il suffisso L:

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

Int64 è un tipo di valore, così colata null per un tipo di valore sarà sempre un'eccezione (NullReferenceException). Che esprimono un Int32 Int64 per riuscirà e non getterà un InvalidCastException.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top