6 Stimmen

Gibt es einen technischen Grund dafür, dass es keine implizite Konvertierung von DBNull zu nullbaren Typen gibt?

Gibt es einen technischen Grund, warum es keine implizite Konvertierung von DBNull zu den verschiedenen nullable und/oder sql-Typen gibt? Ich verstehe, warum die Konvertierungen derzeit nicht stattfinden, aber ich verstehe nicht, warum eine implizite Konvertierung nicht zu der Zeit erstellt oder in späteren Versionen des Frameworks hinzugefügt wurde.

Nur um das klarzustellen: Ich suche nach technischen Gründen, nicht nach "weil sie es so gemacht haben" oder "ich mag es so".

8voto

competent_tech Punkte 43546

Die einfachste Erklärung kommt von Microsofts Dokumentation :

CLR-nullable-Typen sind nicht für die Speicherung von Datenbank-Nullen gedacht, da sich eine ANSI-SQL-Null nicht so verhält wie eine Null-Referenz (oder Nothing in Visual Basic).

3voto

Daniel Pryden Punkte 56882

Nun, ich weiß nicht, ob die SqlTypes Fall, aber es gibt definitiv einige technische Gründe, warum eine implizite Umwandlung zwischen DBNull.Value und Werte von Nullable<T> avec HasValue = false würde nicht funktionieren.

Erinnern Sie sich, DBNull ein Referenztyp ist, und trotz der Tatsache, dass Nullable<T> handelt wie ein Referenztyp - indem er vorgibt, die Funktion eines null Wert - es ist eigentlich ein Werttyp mit Wert-Semantik.

Insbesondere gibt es einen seltsamen Randfall, wenn Werte vom Typ Nullable<T> sind eingepackt. Das Verhalten ist in der Laufzeit speziell auf Werte vom Typ Nullable<T> zu einer Boxversion von T und nicht eine Boxversion von Nullable<T> .

Als die MSDN-Dokumentation erklärt es:

Wenn ein nullable Typ geboxt wird, boxt die Common Language Runtime automatisch den zugrunde liegenden Wert des Nullable(Of T)-Objekts, nicht das Nullable(Of T)-Objekt selbst. Das heißt, wenn die HasValue-Eigenschaft wahr ist, wird der Inhalt der Value-Eigenschaft eingegrenzt. Wenn der zugrundeliegende Wert eines nullable-Typs aus der Box entfernt wird, erstellt die Common Language Runtime eine neue Nullable(Of T)-Struktur, die mit dem zugrundeliegenden Wert initialisiert wird.

Wenn die HasValue-Eigenschaft eines nullbaren Typs false ist, ist das Ergebnis einer Boxing-Operation Nothing. Wird also ein gepackter nullable-Typ an eine Methode übergeben, die ein Objektargument erwartet, muss diese Methode darauf vorbereitet sein, den Fall zu behandeln, dass das Argument Nothing ist. Wenn Nothing in einen nullable-Typ unboxed wird, erstellt die Common Language Runtime eine neue Nullable(Of T)-Struktur und initialisiert deren HasValue-Eigenschaft auf false.

Jetzt stoßen wir auf ein kniffliges Problem: Die C#-Sprachenspezifikation (§4.3.2) besagt, dass wir keine Unboxing-Konvertierung verwenden können, um die DBNull.Value in Nullable<T> :

Für eine Unboxing-Konvertierung zu einer bestimmten nullbar-Typ zur Laufzeit erfolgreich zu sein, muss der Wert des Quelloperanden entweder null oder ein Verweis auf einen verschlüsselten Wert des zugrunde liegenden Nicht-nullbarer-Wert-Typ der nullbar-Typ . Wenn der Quelloperand ein Verweis auf ein inkompatibles Objekt ist, wird ein System.InvalidCastException geworfen wird.

Und wir können keine benutzerdefinierte Konvertierung verwenden, um von object à Nullable<T> auch nicht gemäß § 10.10.3:

Es ist nicht möglich, eine vordefinierte Konvertierung direkt neu zu definieren. Daher dürfen Konvertierungsoperatoren nicht von oder nach object weil es bereits implizite und explizite Konvertierungen zwischen object und alle anderen Typen.

GUT, Sie oder ich konnte es nicht, aber Microsoft konnte die Spezifikation einfach ändern und es legal machen, oder? Das glaube ich nicht.

Warum? Nun, stellen Sie sich den beabsichtigten Anwendungsfall vor: Sie haben eine Methode, die Folgendes zurückgeben soll object . In der Praxis gibt sie entweder DBNull.Value ou int . Aber wie kann der Compiler das wissen? Alles, was er weiß, ist, dass die Methode so spezifiziert ist, dass sie Folgendes zurückgibt object . Und der anzuwendende Konvertierungsoperator muss zur Kompilierzeit ausgewählt werden.

OK, nehmen wir also an, es gibt einen magischen Operator, der von object à Nullable<T> und der Compiler kann auf irgendeine Weise erkennen, wann sie anwendbar ist. (Wir wollen nicht, dass es für jede Methode, die angegeben wird, um Folgendes zurückzugeben object -- was soll sie tun, wenn die Methode tatsächlich einen string ?) Aber wir haben immer noch ein Problem: Die Umwandlung könnte zweideutig sein! Wenn die Methode entweder long , oder DBNull.Value und wir tun es int? v = Method(); Was ist zu tun, wenn die Methode eine boxed long ?

Damit dies wie vorgesehen funktioniert, müssen Sie das Äquivalent von dynamic um den Typ zur Laufzeit zu bestimmen und auf der Grundlage des Laufzeittyps zu konvertieren. Damit haben wir aber eine weitere Regel gebrochen: Da die eigentliche Konvertierung erst zur Laufzeit ausgewählt wird, gibt es keine Garantie, dass sie tatsächlich erfolgreich ist. Aber implizit Konvertierungen sollen keine Ausnahmen auslösen.

An diesem Punkt bedeutet dies nicht nur eine Änderung des spezifizierten Verhaltens der Sprache, sondern auch einen potenziell erheblichen Leistungsabfall, und obendrein könnte es eine unerwartete Ausnahme auslösen! Das scheint ein ziemlich guter Grund zu sein, es nicht zu implementieren. Aber wenn Sie noch einen weiteren Grund brauchen, denken Sie daran, dass jedes Feature am Anfang minus 100 Punkte .

Kurz gesagt: Was Sie hier wirklich wollen, kann mit einer impliziten Konvertierung sowieso nicht erreicht werden. Wenn Sie das Verhalten von dynamic verwenden Sie einfach dynamic ! Dies tut, was Sie wollen, und ist bereits in C# 4.0 implementiert:

object x = 23;
object y = null;

dynamic dx = x;
dynamic dy = y;

int? nx = (int?) dx;
int? ny = (int?) dy;

Console.WriteLine("nx.HasValue = {0}; nx.Value = {1}; ny.HasValue = {2};", 
    nx.HasValue, nx.Value, ny.HasValue);

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X