IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

réflexion: InvokeMemberqui retourne null


Sujet :

C#

  1. #1
    Membre habitué
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2003
    Messages
    93
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2003
    Messages : 93
    Points : 130
    Points
    130
    Par défaut réflexion: InvokeMemberqui retourne null
    Bonjour,

    Exposé de ce que je veux faire:

    > un DaTaGridView: mainDGV
    > son DataSource: une liste d'objets de type T
    > cet objet de type T comporte plusieurs propriétés (dont certaines peuvent être null)

    j'aimerais faire un filtre basique avec n'importe quelle propriété du type de l'objet.

    donc, j'ai ma ComboBox avec la liste des propriété du type
    et un textbox pour la chaine à filtrée...

    pour filtrer ma liste, je fais:

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
     mainDGV.DataSource =
                    maList.FindAll( p =>
                        p.GetType().InvokeMember( c.Name, BindingFlags.GetProperty, null, p, null ).ToString()
                        .Contains( strToSearch ) );

    le c.Name contient le nom de la propriété à appeler.

    Ce code marche sur certaines propriétés qui ne sont pas null.

    Malheureusement, ça peut arriver de tomber sur des propriétés non définie, et dans ce cas, InvokeMember renvoi Null et l'appel de ToString() renvoi NullReferenceException .


    Comment je peux faire en sorte qu'aucune exception ne soit généré afin de pouvoir filtrer comme je le souhaite ?

    Merci.

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    A mon avis, ta technique avec InvokeMember est pas idéale, parce que ça ne te permet pas de vérifier facilement si la propriété existe et si sa valeur n'est pas null

    Il faut gérer plusieurs choses distinctes :
    • Certaines qu'on peut savoir avant de commencer à filtrer
      - si la propriété existe
      - si la propriété peut être null (type valeur ou type référence)
    • Une qu'il faut vérifier pour chaque item : si la propriété renvoie null



    Je vois 2 méthodes pour faire ça bien :

    - La plus simple : d'abord vérifier si la propriété existe, et quel est son type (nullable ou non), et utiliser ça dans ton filtre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    PropertyInfo prop = typeof(TonType).GetProperty(c.Name);
    if (prop != null)
    {
        Func<TonType, bool> filtre;
        // type string : pas la peine d'appeler ToString()
        if (prop.PropertyType == typeof(string))
        {
            filtre = p => (prop.GetValue(p, null) as string ?? String.Empty).Contains(strToSearch);
        }
        // type référence ou Nullable<>
        else if (!p.PropertyType.IsValueType ||
                  p.PropertyType.IsValueType && p.PropertyType.IsGenericType && p.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            filtre = p => (prop.GetValue(p, null) ?? String.Empty).ToString().Contains(strToSearch);
        }
        // type valeur, ne sera jamais null
        else
        {
            filtre = p => prop.GetValue(p, null).ToString().Contains(strToSearch);
        }
        mainDGV.DataSource = maList.FindAll(filtre);
    }
    else
    {
        // La propriété n'existe pas, on ne filtre pas :
        mainDGV.DataSource = maList;    
    }
    - La plus compliquée, mais peut-être un peu meilleure au niveau performance si la liste est longue : en générant le filtre dynamiquement avec une expression Linq :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    Func<TonType> filtre = GetFilter<TonType>(c.Name, strToSearch);
    mainDGV.DataSource = maList.FindAll(filtre);
     
     
    ...
     
    private Expression<Func<T, bool>> GetFilter<T>(string propertyName, string strToSearch)
    {
        PropertyInfo prop = typeof(T).GetProperty(propertyName);
        if (prop != null)
        {
            // On construit une expression pour implémenter le filtre :
     
            // Paramètre de l'expression
            ParameterExpression param = Expression.Parameter(typeof(T), "p");
     
            // Accès à la propriété
            Expression value = Expression.Property(param, prop); // p.<property>
            // Si le type de la propriété est un type référence ou un type nullable (Nullable<T>), on va utiliser l'opérateur ??
            if (!prop.PropertyType.IsValueType ||
                prop.PropertyType.IsValueType && prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                Expression emptyString =
                    Expression.Convert(
                        Expression.Field(null, typeof(string).GetField("Empty")),
                        typeof(object));
     
                value = Expression.Coalesce(                    // p.<property> ?? String.Empty
                            value,                              // p.<property>
                            emptyString);                       // String.Empty
            }
            // Si la propriété n'est pas de type String, on appelle ToString :
            if (prop.PropertyType == typeof(string))
            {
                value = Expression.Convert(value, typeof(string));
            }
            else
            {
                value = Expression.Call(value, "ToString", new Type[] {});
            }
     
            // Appel de la méthode Contains
            Expression contains = 
                Expression.Call(
                    value,
                    "Contains",
                    new Type[] {},
                    Expression.Constant(strToSearch));
     
            // Expression finale
            return Expression.Lambda<Func<T, bool>>(contains, param);
        }
        else
        {
            // La propriété n'existe pas, on renvoie un filtre qui laisse tout passer :
            return item => true;
        }
    }
    Bon, j'avoue, la 2e méthode c'est un peu bulldozer... mais ça m'amusait de le faire


    Au fait, c'est quoi le rapport avec la sérialisation (cf. titre) ?

  3. #3
    Membre habitué
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2003
    Messages
    93
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2003
    Messages : 93
    Points : 130
    Points
    130
    Par défaut
    wouah, merci beaucoup!

    c'est vrai que j'avais pas pensé à déclarer un objet de type Func<>
    je connais pas trop ce type, je vais lire la doc à son sujet.

    pour le:
    - si la propriété existe

    > à priori, c'est déjà vérifié avant puisque les items du ComboBox proviennent d'une liste qui contient tout les PropertyInfo.Name du type en question
    mais, le code est plus robuste comme ça il est vrai.

    pour la 2e méthode... j'ai pas vraiment compliqué.
    C'est pas de mon niveau je crois, je connais absolument pas le type Expression..
    mais c'est sur qu'elle parait meilleure niveau performance

    j'étudierais tout ça en détail

    merci et résolu !

    Au fait, c'est quoi le rapport avec la sérialisation (cf. titre) ?
    ben pour moi, le InvokeMember fait partie de la sérialisation... mais c'est vrai que c'était peut-être pas judicieux


    edit:
    pour la 1ere méthode, le:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    mainDGV.DataSource = maList.FindAll(filtre);
    ne marche pas (erreur de compilation)
    par contre en faisant un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    maList.Where(filtre).ToList();
    ça compile

  4. #4
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Citation Envoyé par flolem Voir le message
    ben pour moi, le InvokeMember fait partie de la sérialisation...
    Non, tu confonds avec la "réflexion"
    La sérialisation, c'est le fait de transformer un objet sous une forme qui permet de le stocker ou de le transférer (en binaire, XML, etc)

    Citation Envoyé par flolem Voir le message
    pour la 1ere méthode, le:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    mainDGV.DataSource = maList.FindAll(filtre);
    ne marche pas (erreur de compilation)
    par contre en faisant un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    maList.Where(filtre).ToList();
    ça compile
    Ah ok, c'est parce que FindAll ne prend pas en paramètre un Func<T, bool> mais un Predicate<T>. Les 2 delegates ont la même signature, mais le compilateur ne s'en rend pas compte

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Findcontrol retourne null
    Par Kiwi_violet dans le forum ASP.NET
    Réponses: 1
    Dernier message: 13/04/2007, 12h21
  2. TTF_OpenFont() retournant NULL
    Par FabaCoeur dans le forum SDL
    Réponses: 4
    Dernier message: 11/04/2007, 16h30
  3. GetDC retourne NULL Oo
    Par Groove dans le forum OpenGL
    Réponses: 3
    Dernier message: 02/03/2007, 17h46
  4. Réponses: 3
    Dernier message: 02/03/2007, 11h41
  5. opérateur + dans SELECT retourne null ?
    Par david_chardonnet dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 19/01/2007, 10h47

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo