C # Type Inference Ruft den falschen Typen
Frage
Ich habe die folgende Eigenschaft, die eine InvalidCastException
warf, wenn der Getter zugegriffen wurde, als ViewState[TOTAL_RECORD_COUNT]
null
war.
public long TotalRecordCount
{
get { return (long)(ViewState[TOTAL_RECORD_COUNT] ?? -1); }
set { ViewState[TOTAL_RECORD_COUNT] = value; }
}
Mein Gedanke ist, dass es falsch das Objekt in ViewState[TOTAL_RECORD_COUNT]
zu einem int
unbox versucht, die scheiterten, weil es eine long
enthalten ist, aber ich denke, es könnte ein Fehler in dieser Logik sein. Ich werde es als Übung dem Leser überlassen, dass Fehler hinweisen.
ich da geändert haben, dass das Eigentum zu lesen
public long TotalRecordCount
{
get { return (long?)ViewState[TOTAL_RECORD_COUNT] ?? -1; }
set { ViewState[TOTAL_RECORD_COUNT] = value; }
}
die funktioniert nur anschwellen. Dennoch frage ich mich links, was mit meiner ursprünglichen Version ... Stackoverflow zur Rettung falsch war?
Beachten Sie, dass, wenn ich versuche, (long)(ViewState[TOTAL_RECORD_COUNT] ?? -1)
in dem Direkt-Fenster auszuführen, ich die Fehlermeldung Cannot unbox 'ViewState[TOTAL_RECORD_COUNT] ?? -1' as a 'long'
bekommen und wenn ich (ViewState[TOTAL_RECORD_COUNT] ?? -1).GetType().Name
ausführen ich Int32
bekommen. Ich kann (long)-1
ausführen und mit -1 als Int64
am Ende ... so etwas ist los?
Lösung
Der Rückgabetyp ViewState
Indexer ist Object
(ich nehme an, Sie ASP.NET Ansichtszustand bedeutet hier). Betrachten wir nun, was der Compiler zu tun hat, wenn er dies sieht (die Code entspricht):
object o = ViewState[...];
var x = o ?? -1;
Es hat den Ergebnistyp des Ausdrucks o ?? -1
irgendwie abzuleiten. Auf der linken Seite sieht er eine object
, auf der rechten Seite ein int
ist. Offensichtlich ist die allgemeinste Art für diesen Ausdruck auch object
. Dies bedeutet jedoch, dass, wenn es tatsächlich endet mit, dass -1
(weil o
null war), wird es konvertieren müssen object
-. Und für einen int
, bedeutet dies, Boxen
So x
vom Typ object
ist, und es kann eine int
enthalten (und vielleicht auch einige andere integrale Art - wir wissen nicht, was in Ihrem Ansichtszustand ist, könnte es short
, zum Beispiel). Jetzt schreiben Sie:
long y = (long)x;
Da x
object
ist, dann ist dies Unboxing. Sie kann jedoch nur unbox Werttypen in genau die gleiche Art (mit der einzigen Ausnahme, dass Sie einen signierten Typen für einen äquivalenten Typen ohne Vorzeichen ersetzen können, und ENUM für den zugrunde liegenden Basistyp). Das heißt, man kann nicht unbox int
in long
. Ein weitaus einfacher Weg, dies zu repro, ohne „Extra“ Code wäre:
object x = 123;
long y = (long)x;
Welche auch InvalidCastException
wirft, und für genau die gleichen Grund.
Andere Tipps
Ein Abguss hat einen Schritt nur sein.
Der Ausdruck <object> ?? <int>
produziert ein anderes Objekt, und wenn der erste Wert ist Null, das heißt. ViewState[TOTAL_RECORD_COUNT]
null ist, dann wird der resultierende Wert wird in eine Aufgabe, mit einem boxed Int32 sein.
Da kann man kein Objekt unbox ein Int32 auf eine lange enthält, müssen Sie es zunächst zu einem Int32 unbox und warf sie dann auf eine lange.
Die Probleme nicht die Unboxing der ViewState[TOTAL_RECORD_COUNT]
sind, das Problem ist die Boxen und Unboxing der -1.
ViewState[TOTAL_RECORD_COUNT] ?? -1
Sie benutzen die ?? Betreiber auf „Objekt“ und „int“. Die sich ergebende Typ „Objekt“. Das bedeutet, die -1 wird (wie int) eingerahmt werden, wenn das Feld in dem Ansichtszustand nicht vorhanden ist.
Dann wird Ihr Programm stürzt später, wenn es versucht, die (int) -1 als lange unbox.
In Ihrem ursprünglichen, wenn Sie es brechen, das Sie tun:
(ViewState[TOTAL_RECORD_COUNT] ?? -1)
Die null-Koaleszenz-Operator (??) ist specifially entwickelt, um:
einen Standardwert für ein Nullable-Wertetypen sowie Referenztypen zu definieren.
In Ihrem Fall, Sie verwenden eine System.Object zu handhaben, so es geht um Ihre „-1“ zu nehmen, behandelt es als Int32, und das Feld in eine neue System.Object. Dann wird versucht, den Int32 in eine langen unbox, die fehlschlägt, da die Besetzung nicht unbox kann und die Art in einem einzigen Schritt ändern.
Sie können dieses Problem lösen leicht durch die Angabe, dass Ihre -1 unter Verwendung des L-Suffix ein lang ist:
public long TotalRecordCount
{
get { return (long)(ViewState[TOTAL_RECORD_COUNT] ?? -1L); }
set { ViewState[TOTAL_RECORD_COUNT] = value; }
}
Int64 ist ein Werttyp, so Casting null
auf einen Wert Typ wird immer eine Ausnahme (NullReferenceException
) werfen. Und ein Int32 zu Int64 Casting erfolgreich sein wird und nicht ein InvalidCastException
werfen.