Question

J'ai créé la propriété suivante qui a jeté InvalidCastException si le getter est accessible lorsque ViewState[TOTAL_RECORD_COUNT] était null.

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

Ma pensée est que mal tenté de unbox l'objet ViewState[TOTAL_RECORD_COUNT] à un int, qui a échoué parce qu'elle contenait une long, mais je pense qu'il pourrait y avoir une faille dans cette logique. Je laisse comme un exercice au lecteur de signaler ce défaut.

J'ai changé depuis que la propriété à lire

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

qui gonflent les œuvres justes. Pourtant, je me demande à gauche ce qui clochait avec ma version originale ... StackOverflow à la rescousse?

Notez que si je tente d'exécuter (long)(ViewState[TOTAL_RECORD_COUNT] ?? -1) dans la fenêtre immédiate, je reçois le message d'erreur Cannot unbox 'ViewState[TOTAL_RECORD_COUNT] ?? -1' as a 'long' et si j'exécute (ViewState[TOTAL_RECORD_COUNT] ?? -1).GetType().Name je reçois Int32. Je peux exécuter (long)-1 et finissent avec -1 comme Int64 ... Alors quoi de neuf?

Était-ce utile?

La solution

Le type de retour de ViewState indexeur est Object (je suppose que vous ici dire ASP.NET ViewState). Considérons maintenant ce que le compilateur doit faire quand il voit (ce qui équivaut à votre code):

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

Il doit déduire le type de résultat de l'expression o ?? -1 en quelque sorte. Sur la gauche, il voit un object, à droite est un int. De toute évidence, le type le plus général de cette expression est également object. Cependant, cela signifie que si elle finit réellement utiliser cette -1 (parce que o était nulle), il devra le convertir en object -. Et pour un int, cela signifie que la boxe

x est de type object, et il peut contenir un int (et peut-être aussi un autre type intégral - nous ne savons pas ce qui est dans votre état d'affichage, il pourrait être short, par exemple). Maintenant, vous écrivez:

long y = (long)x;

Depuis x est object, c'est unboxing. Cependant, vous ne pouvez les types de valeurs Unbox en même type exact (à la seule exception étant que vous pouvez substituer un type signé pour un équivalent de type non signé, et pour son type ENUM de base sous-jacente). Autrement dit, vous ne pouvez pas Unbox int en long. Une façon beaucoup plus simple de repro cela, sans code « extra », serait:

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

Ce qui jette également InvalidCastException, et pour cette raison exacte.

Autres conseils

Une distribution doit être une étape uniquement.

Le <object> ?? <int> d'expression produira un autre objet, et lorsque la première valeur est nulle, ie. ViewState[TOTAL_RECORD_COUNT] est nulle, alors la valeur résultante sera un objet, avec un Int32 boîte en elle.

Puisque vous ne pouvez pas unbox un objet contenant un Int32 à une longue, vous devez d'abord Unbox à un Int32, puis le jeter à une longue.

Les problèmes ne sont pas les unboxing du ViewState[TOTAL_RECORD_COUNT], le problème est la boxe et unboxing de -1.

   ViewState[TOTAL_RECORD_COUNT] ?? -1

Vous utilisez ?? opérateur sur « objet » et « int ». Le type résultant est « objet ». Cela signifie que le -1 sera emballé (int) lorsque le champ n'existe pas dans l'état d'affichage.

Ensuite, votre programme se bloque plus tard, quand il essaie de unbox le (int) -1 comme un long.

Dans l'original, si vous cassez vers le bas, vous faites:

(ViewState[TOTAL_RECORD_COUNT] ?? -1)

opérateur null-coalescent (??) est specifially conçu pour:

  

pour définir une valeur par défaut d'un des types valeur nullable ainsi que types de référence.

Dans votre cas, vous l'utilisez pour gérer un System.Object, donc il va prendre votre « -1 », le traiter comme un Int32, et la boîte dans une nouvelle System.Object. Ensuite, il essaie de unbox l'Int32 dans une longue, qui échoue, car la distribution ne peut Unbox et changer le type en une seule étape.

Vous pouvez résoudre facilement en précisant que votre -1 est un long en utilisant le suffixe L:

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

Int64 est un type de valeur, de sorte que la coulée null à un type de valeur sera toujours jeter une exception (NullReferenceException). Et jetant un Int32 à Int64 réussira et ne lancera pas d'InvalidCastException.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top