150 votes

Le moyen le plus efficace de vérifier la présence de DBNull et de l'assigner à une variable ?

Cette question revient de temps en temps, mais je n'ai pas vu de réponse satisfaisante.

Un modèle typique est (la ligne est un DataRow ) :

 if (row["value"] != DBNull.Value)
 {
      someObject.Member = row["value"];
 }

Ma première question est de savoir lequel est le plus efficace (j'ai inversé la condition) :

  row["value"] == DBNull.Value; // Or
  row["value"] is DBNull; // Or
  row["value"].GetType() == typeof(DBNull) // Or... any suggestions?

Ce site indique que .GetType() devrait être plus rapide, mais peut-être le compilateur connaît-il quelques astuces que je ne connais pas ?

Deuxième question, cela vaut-il la peine de mettre en cache la valeur de row["value"] ou le compilateur optimise-t-il l'indexeur de toute façon ?

Par exemple :

  object valueHolder;
  if (DBNull.Value == (valueHolder = row["value"])) {}

Notes :

  1. row["value"] existe.
  2. Je ne connais pas l'index de la colonne (d'où la recherche par nom de colonne).
  3. Ma question porte spécifiquement sur la vérification de l'absence de DBNull, puis sur l'affectation (et non sur l'optimisation prématurée, etc.).

J'ai évalué quelques scénarios (temps en secondes, 10.000.000 d'essais) :

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 a les mêmes performances que "==".

Le résultat le plus intéressant ? Si le nom de la colonne ne correspond pas à la casse (par exemple, "Value" au lieu de "value"), il faut environ dix fois plus de temps (pour une chaîne de caractères) :

row["Value"] == DBNull.Value: 00:00:12.2792374

La morale de l'histoire semble être que si vous ne pouvez pas rechercher une colonne par son index, assurez-vous que le nom de la colonne que vous donnez à l'indexeur correspond exactement au nom de la DataColumn.

La mise en cache de la valeur semble également être presque deux fois aussi vite :

No Caching: 00:00:03.0996622
With Caching: 00:00:01.5659920

Donc la méthode la plus efficace semble pour être :

 object temp;
 string variable;
 if (DBNull.Value != (temp = row["value"]))
 {
      variable = temp.ToString();
 }

71voto

Dan Tao Points 60518

Je dois manquer quelque chose. Est-ce que la vérification de DBNull exactement ce que le DataRow.IsNull de la méthode ?

J'ai utilisé les deux méthodes d'extension suivantes :

public static T? GetValue<T>(this DataRow row, string columnName) where T : struct
{
    if (row.IsNull(columnName))
        return null;

    return row[columnName] as T?;
}

public static string GetText(this DataRow row, string columnName)
{
    if (row.IsNull(columnName))
        return string.Empty;

    return row[columnName] as string ?? string.Empty;
}

Utilisation :

int? id = row.GetValue<int>("Id");
string name = row.GetText("Name");
double? price = row.GetValue<double>("Price");

Si tu ne voulais pas Nullable<T> les valeurs de retour pour GetValue<T> vous pourriez facilement retourner default(T) ou une autre option à la place.


Dans un autre ordre d'idées, voici une alternative VB.NET à la suggestion de Stevo3000 :

oSomeObject.IntMember = If(TryConvert(Of Integer)(oRow("Value")), iDefault)
oSomeObject.StringMember = If(TryCast(oRow("Name"), String), sDefault)

Function TryConvert(Of T As Structure)(ByVal obj As Object) As T?
    If TypeOf obj Is T Then
        Return New T?(DirectCast(obj, T))
    Else
        Return Nothing
    End If
End Function

34voto

Jon Grant Points 7560

Vous devez utiliser la méthode :

Convert.IsDBNull()

Étant donné qu'il est intégré au Framework, je m'attendrais à ce que ce soit le plus efficace.

Je suggérerais quelque chose du genre :

int? myValue = (Convert.IsDBNull(row["column"]) ? null : (int?) Convert.ToInt32(row["column"]));

Et oui, le compilateur devrait le mettre en cache pour vous.

20voto

Marc Gravell Points 482669

Le compilateur n'optimisera pas l'indexeur (par exemple, si vous utilisez deux fois row["value"]), donc oui, c'est le cas. légèrement plus rapide à faire :

object value = row["value"];

et ensuite utiliser la valeur deux fois ; utiliser .GetType() risque de poser des problèmes si elle est nulle...

DBNull.Value est en fait un singleton, donc pour ajouter une 4ème option - vous pourriez peut-être utiliser ReferenceEquals - mais en réalité, je pense que vous vous inquiétez trop ici... Je ne pense pas que la différence de vitesse entre "is", "==" etc. soit la cause du problème de performance que vous rencontrez. Profilage de l'ensemble de votre code et de te concentrer sur quelque chose qui compte... ce ne sera pas ça.

9voto

Stevo3000 Points 12725

J'utiliserais le code suivant en C# ( VB.NET n'est pas aussi simple).

Le code attribue la valeur si elle n'est pas null/DBNull, sinon il attribue la valeur par défaut qui pourrait être définie comme la valeur LHS permettant au compilateur d'ignorer l'attribution.

oSomeObject.IntMemeber = oRow["Value"] as int? ?? iDefault;
oSomeObject.StringMember = oRow["Name"] as string ?? sDefault;

7voto

Saleh Najar Points 1

Il y a le cas gênant où l'objet pourrait être une chaîne de caractères. Le code de la méthode d'extension ci-dessous gère tous les cas. Voici comment l'utiliser :

    static void Main(string[] args)
    {
        object number = DBNull.Value;

        int newNumber = number.SafeDBNull<int>();

        Console.WriteLine(newNumber);
    }

    public static T SafeDBNull<T>(this object value, T defaultValue) 
    {
        if (value == null)
            return default(T);

        if (value is string)
            return (T) Convert.ChangeType(value, typeof(T));

        return (value == DBNull.Value) ? defaultValue : (T)value;
    } 

    public static T SafeDBNull<T>(this object value) 
    { 
        return value.SafeDBNull(default(T)); 
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X