Diese Frage taucht gelegentlich auf, aber ich habe keine zufriedenstellende Antwort gesehen.
Ein typisches Muster ist (Zeile ist eine DataRow ):
if (row["value"] != DBNull.Value)
{
someObject.Member = row["value"];
}
Meine erste Frage ist, welche effizienter ist (ich habe die Bedingung umgedreht):
row["value"] == DBNull.Value; // Or
row["value"] is DBNull; // Or
row["value"].GetType() == typeof(DBNull) // Or... any suggestions?
Dies zeigt an, dass .GetType () schneller sein sollte, aber vielleicht kennt der Compiler ein paar Tricks, die ich nicht kenne?
Zweite Frage: Lohnt es sich, den Wert von row ["value"] zwischenzuspeichern, oder optimiert der Compiler den Indexer trotzdem?
Beispielsweise:
object valueHolder;
if (DBNull.Value == (valueHolder = row["value"])) {}
Anmerkungen:
- Zeile ["Wert"] existiert.
- Ich kenne den Spaltenindex der Spalte nicht (daher die Suche nach Spaltennamen).
- Ich frage speziell nach DBNull und dann nach Zuweisung (nicht nach vorzeitiger Optimierung usw.).
Ich habe einige Szenarien verglichen (Zeit in Sekunden, 10.000.000 Versuche):
row["value"] == DBNull.Value: 00:00:01.5478995
row["value"] is DBNull: 00:00:01.6306578
row["value"].GetType() == typeof(DBNull): 00:00:02.0138757
Object.ReferenceEquals hat die gleiche Leistung wie "=="
Das interessanteste Ergebnis? Wenn Sie den Namen der Spalte von Fall zu Fall nicht übereinstimmen (z. B. "Wert" anstelle von "Wert"), dauert dies ungefähr zehnmal länger (für eine Zeichenfolge):
row["Value"] == DBNull.Value: 00:00:12.2792374
Die Moral der Geschichte scheint zu sein: Wenn Sie eine Spalte nicht anhand ihres Index nachschlagen können, stellen Sie sicher, dass der Spaltenname, den Sie dem Indexer zuführen, genau mit dem Namen der DataColumn übereinstimmt.
Das Zwischenspeichern des Werts scheint ebenfalls fast doppelt so schnell zu sein:
No Caching: 00:00:03.0996622
With Caching: 00:00:01.5659920
Die effizienteste Methode scheint also zu sein:
object temp;
string variable;
if (DBNull.Value != (temp = row["value"]))
{
variable = temp.ToString();
}
IDataRecord
Erweiterungen geliebt .Antworten:
Mir muss etwas fehlen. Prüft man nicht
DBNull
genau, was dieDataRow.IsNull
Methode macht?Ich habe die folgenden zwei Erweiterungsmethoden verwendet:
Verwendung:
Wenn Sie keine
Nullable<T>
Rückgabewerte für möchtenGetValue<T>
, können Sie auch einfachdefault(T)
eine andere Option zurückgeben.In einem anderen Zusammenhang ist hier eine VB.NET-Alternative zu Stevo3000s Vorschlag:
quelle
row.IsNull(columnName)
Sie schreiben , lesen Sie es bereits einmal und lesen es erneut. Nicht zu sagen, dass das einen Unterschied machen wird, aber theoretisch kann es weniger effizient sein ..System.Data.DataSetExtensions.DataRowExtensions.Field<T>(this System.Data.DataRow, string)
Wesentlichen dasselbe wie bei der ersten Methode?Sie sollten die Methode verwenden:
In Anbetracht dessen, dass es in das Framework integriert ist, würde ich erwarten, dass dies am effizientesten ist.
Ich würde etwas vorschlagen in der Art von:
Und ja, der Compiler sollte es für Sie zwischenspeichern.
quelle
Der Compiler optimiert den Indexer nicht weg (dh wenn Sie Zeile ["Wert"] zweimal verwenden), also ist es etwas schneller:
und dann Wert zweimal verwenden; Die Verwendung von .GetType () birgt Probleme, wenn es null ist ...
DBNull.Value
ist eigentlich ein Singleton, also um eine vierte Option hinzuzufügen - Sie könnten vielleicht ReferenceEquals verwenden -, aber in Wirklichkeit machen Sie sich hier zu viele Sorgen ... Ich glaube nicht, dass die Geschwindigkeit zwischen "is", "==" unterschiedlich ist "etc wird die Ursache für jedes Leistungsproblem sein, das Sie sehen. Profilieren Sie Ihren gesamten Code und konzentrieren Sie sich auf etwas, das wichtig ist ... das wird es nicht sein.quelle
Ich würde den folgenden Code in C # verwenden ( VB.NET ist nicht so einfach).
Der Code weist den Wert zu, wenn er nicht null / DBNull ist. Andernfalls weist er den Standard zu, der auf den LHS-Wert festgelegt werden kann, sodass der Compiler die Zuweisung ignorieren kann.
quelle
oSomeObject.IntMember = If(TryCast(oRow("Value), Integer?), iDefault)
.TryCast
dies nicht die gleiche praktische Funktionalität bietet wie deras
Operator von C # fürNullable(Of T)
Typen. Der beste Weg, dies nachzuahmen, besteht darin, eine eigene Funktion zu schreiben, wie ich jetzt in meiner Antwort vorgeschlagen habe.Ich bin der Meinung, dass nur sehr wenige Ansätze hier die Aussicht auf OP am meisten gefährden (Marc Gravell, Stevo3000, Richard Szalay, Neil, Darren Koppand) und die meisten unnötig komplex sind. Wenn Sie sich bewusst sind, dass dies eine nutzlose Mikrooptimierung ist, sollten Sie diese grundsätzlich anwenden:
1) Lesen Sie den Wert nicht zweimal aus DataReader / DataRow. Zwischenspeichern Sie ihn also entweder vor Nullprüfungen und Casts / Conversions oder übergeben Sie Ihr
record[X]
Objekt besser direkt an eine benutzerdefinierte Erweiterungsmethode mit der entsprechenden Signatur.2) Um dies zu befolgen, verwenden Sie keine integrierte
IsDBNull
Funktion in Ihrem DataReader / DataRow, da dies denrecord[X]
internen Aufruf aufruft. Sie werden dies also tatsächlich zweimal tun.3) Der Typvergleich ist in der Regel immer langsamer als der Wertevergleich. Mach
record[X] == DBNull.Value
es einfach besser.4) Direktes Casting ist schneller als das Aufrufen der
Convert
Klasse zum Konvertieren, obwohl ich befürchte, dass letztere weniger ins Stocken geraten wird.5) Schließlich ist der Zugriff auf Datensätze über den Index und nicht über den Spaltennamen wieder schneller.
Ich denke, die Ansätze von Szalay, Neil und Darren Koppand werden besser sein. Ich mag besonders den Ansatz der Erweiterungsmethode von Darren Koppand, der den Index- / Spaltennamen berücksichtigt
IDataRecord
(obwohl ich ihn gerne weiter eingrenzen möchteIDataReader
).Achten Sie darauf, es zu nennen:
und nicht
falls Sie zwischen
0
und unterscheiden müssenDBNull
. Wenn Sie beispielsweise Nullwerte in Aufzählungsfeldern haben, besteht andernfalls diedefault(MyEnum)
Gefahr, dass der erste Aufzählungswert zurückgegeben wird. Also besser anrufenrecord.GetColumnValue<MyEnum?>("Field")
.Da Sie aus a lesen
DataRow
, würde ich eine Erweiterungsmethode für beideDataRow
undIDataReader
durch DRYing von allgemeinem Code erstellen.Nennen wir es jetzt wie folgt:
Ich glaube , das ist , wie es im Rahmen hätte sein sollen (statt dem
record.GetInt32
,record.GetString
etc Methoden) in erster Linie - keine Laufzeitausnahmen und gibt uns die Flexibilität, Griff Nullwerte.Aus meiner Erfahrung hatte ich weniger Glück mit einer generischen Methode zum Lesen aus der Datenbank. Ich hatte immer zu benutzerdefinierten Griff verschiedene Typen, so dass ich meine eigene schreiben hatte
GetInt
,GetEnum
,GetGuid
etc. Methoden auf lange Sicht. Was ist, wenn Sie beim Lesen von Zeichenfolgen aus db standardmäßig Leerzeichen kürzen oderDBNull
als leere Zeichenfolge behandeln möchten? Oder wenn Ihre Dezimalstelle von allen nachgestellten Nullen abgeschnitten werden soll. Ich hatte die meisten Probleme mit demGuid
Typ, bei dem sich verschiedene Connector-Treiber unterschiedlich verhalten haben, auch wenn zugrunde liegende Datenbanken sie als Zeichenfolge oder Binärdatei speichern können. Ich habe eine Überlastung wie diese:Mit dem Ansatz von Stevo3000 finde ich das Aufrufen etwas hässlich und langweilig, und es wird schwieriger sein, daraus eine generische Funktion zu machen.
quelle
Es gibt den problematischen Fall, dass das Objekt eine Zeichenfolge sein könnte. Der folgende Code für die Erweiterungsmethode behandelt alle Fälle. So würden Sie es verwenden:
quelle
Ich persönlich bevorzuge diese Syntax, die die explizite IsDbNull-Methode verwendet, die von verfügbar gemacht wird
IDataRecord
, und den Spaltenindex zwischenspeichert, um eine doppelte Suche nach Zeichenfolgen zu vermeiden.Aus Gründen der Lesbarkeit erweitert, geht es ungefähr so:
Aus Gründen der Kompaktheit des DAL-Codes in eine einzelne Zeile umgeschrieben. Beachten Sie, dass wir in diesem Beispiel
int bar = -1
if zuweisen, wennrow["Bar"]
es null ist.Die Inline-Zuweisung kann verwirrend sein, wenn Sie nicht wissen, dass sie vorhanden ist, aber sie hält den gesamten Vorgang in einer Zeile, was meiner Meinung nach die Lesbarkeit verbessert, wenn Sie Eigenschaften aus mehreren Spalten in einem Codeblock auffüllen.
quelle
Nicht, dass ich das getan hätte, aber Sie könnten den Doppelindexaufruf umgehen und trotzdem Ihren Code mithilfe einer statischen / Erweiterungsmethode sauber halten.
Dh.
Dann:
Hat auch den Vorteil, dass die Nullprüflogik an einem Ort bleibt. Nachteil ist natürlich, dass es sich um einen zusätzlichen Methodenaufruf handelt.
Nur ein Gedanke.
quelle
Ich versuche, diese Überprüfung so weit wie möglich zu vermeiden.
Offensichtlich muss dies nicht für Spalten durchgeführt werden, die nicht enthalten sind
null
.Wenn Sie in einem Nullable-Werttyp (
int?
usw.) speichern , können Sie einfach mit konvertierenas int?
.Wenn Sie nicht zwischen
string.Empty
und unterscheiden müssennull
, können Sie einfach anrufen.ToString()
, da DBNull zurückkehrtstring.Empty
.quelle
Ich benutze immer:
Fand es kurz und umfassend.
quelle
So gehe ich mit dem Lesen aus DataRows um
Anwendungsbeispiel:
Requisiten für Monster haben mein .Net für ChageTypeTo-Code erhalten.
quelle
Ich habe etwas Ähnliches mit Erweiterungsmethoden gemacht. Hier ist mein Code:
Um es zu benutzen, würden Sie so etwas tun
quelle
Wenn in einer DataRow die Zeile ["Feldname"] isDbNull ist, ersetzen Sie sie durch 0, andernfalls erhalten Sie den Dezimalwert:
quelle
benutze so
quelle
Ich habe IsDBNull in einem Programm, das viele Daten aus einer Datenbank liest. Mit IsDBNull werden Daten in ca. 20 Sekunden geladen. Ohne IsDBNull ca. 1 Sekunde.
Ich denke, es ist besser zu verwenden:
quelle