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 :

Cast de liste générique


Sujet :

C#

  1. #1
    Membre éclairé Avatar de ppphil
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Juin 2007
    Messages : 612
    Points : 685
    Points
    685
    Par défaut Cast de liste générique
    Bonjour,
    Voilà, j'expose le problème :
    soit deux objets :
    - un objet A contenant une EntityCollection<T>
    - un objet B du même type T
    - une fonction dans laquelle je dois ajouter l'objet B dans la EntityCollection<T> de l'objet A
    - la fonction prend en paramètres les deux objets ci-dessus et le nom de la propriété de l'objet A à laquelle ajouter l'objet B
    - je connais le nom de la propriété de type EntityCollection<T> à laquelle je dois ajouter l'objet de type T
    - je sais que le premier objet contient une EntityCollection<T>

    Dans ma fonction, par réflexion, j'arrive à retrouver la propriété de type EntityCollection<T> grâce à son nom.
    Maintenant que j'ai cette propriété, j'aimerais pouvoir y ajouter l'objet B.
    Mais pour cela, il me faut caster la propriété pour obtenir la fonction Add() et caster l'objet B pour pouvoir le passer à la fonction Add()

    Ca ressemblerait à ceci :
    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
     
      public class Obj1 : EntityObject
      {
        public string Name;
      }
     
      public class Obj2 : EntityObject
      {
        public string Name;
      }
     
      public class ObjetA1
      {
        public EntityCollection<Obj1> LstO1 { get; set; }
     
        public ObjetA1()
        {
          LstO1 = new EntityCollection<Obj1>();
        }
      }
     
      public class ObjetA2
      {
        public EntityCollection<Obj2> LstO2 { get; set; }
     
        public ObjetA2()
        {
          LstO2 = new EntityCollection<Obj2>();
        }
      }
     
        private void AddToList(object objA, object objB, string propName)
        {
          PropertyInfo pi = objA.GetType().GetProperty(propName);
     
          ((EntityCollection<Obj1>)pi.GetValue(objA, null)).Add((Obj1)objB);
        }
     
        private void button4_Click(object sender, EventArgs e)
        {
          ObjetA1 obj = new ObjetA1();
          Obj1 obj1 = new Obj1();
          obj1.Name = "OBJ1";
          AddToList(obj, obj1, "LstO1");
        }
    Dans AddToList il faut que je puisse remplacer Obj1 par le type réel (génériquement) de l'objet objB.
    Et là je bute sur ces cast...
    Ca devrait être possible mais il y quelque chose que je fais de travers...
    Quelqu'un aurait-il une idée de la bonne méthode à employer ??

  2. #2
    Membre actif Avatar de polkduran
    Profil pro
    Consultant informatique
    Inscrit en
    Décembre 2009
    Messages
    155
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2009
    Messages : 155
    Points : 275
    Points
    275
    Par défaut
    Essaies quelque chose comme ça, tu tireras avantage de la généricité
    Code C# : 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
    public class Obj1 : EntityObject
      {
        public string Name;
      }
     
      public class Obj2 : EntityObject
      {
        public string Name;
      }
     
      public class ObjetA<T> where T:EntityObject
      {
        public EntityCollection<T> LstO { get; set; }
     
        public ObjetA1()
        {
          LstO = new EntityCollection<T>();
        }
     
        public void AddObj(T obj){
            this.ListO.add(obj);
        }
     
      }
     
     
     
        private void button4_Click(object sender, EventArgs e)
        {
          ObjetA<Obj1> objA1 = new ObjetA<Obj1>();
          Obj1 obj1 = new Obj1();
          objA1.add(obj1);
     
          ObjetA<Obj2> objA2 = new ObjetA<Obj2>();
          Obj2 obj2 = new Obj2();
          objA2.add(obj2);
        }

  3. #3
    Membre éclairé Avatar de ppphil
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Juin 2007
    Messages : 612
    Points : 685
    Points
    685
    Par défaut
    le problème est que ObjA1 et ObjA2 sont déjà déclarés et que je ne peux pas y toucher....

  4. #4
    Membre actif Avatar de polkduran
    Profil pro
    Consultant informatique
    Inscrit en
    Décembre 2009
    Messages
    155
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2009
    Messages : 155
    Points : 275
    Points
    275
    Par défaut
    c'est vrai que t'es alors un peu limité,

    tu peux faire sinon

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
     
    private void AddToList<T>(EntityCollection<T> List,T obj) where T:EntityObject{
        List.add(obj);
    }
     
     private void button4_Click(object sender, EventArgs e)
        {
          ObjetA1 obj = new ObjetA1();
          Obj1 obj1 = new Obj1();
          AddToList<Obj1>(obj.LstO1 , obj1);
        }

    C'est plus propre que la reflection

  5. #5
    Membre éclairé Avatar de ppphil
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Juin 2007
    Messages : 612
    Points : 685
    Points
    685
    Par défaut
    C'est bon, j'ai trouvé...
    Une EntityCollection<TEntity> demande un type bien précis. Par contre, elle implémente un ICollection<T> qui ne demande pas de précision quant au type.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     EntityCollection<TEntity> : RelatedEnd, ICollection<TEntity>, IEnumerable<TEntity>, IEnumerable, IListSource where TEntity : class, System.Data.Objects.DataClasses.IEntityWithRelationships
    Alors la solution est la suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
        private void AddToEntityCollection<T>(object objA, T objB, string propName)
        {
          PropertyInfo pi = objA.GetType().GetProperty(propName);
          ((ICollection<T>)pi.GetValue(objA, null)).Add(objB);
        }
    Merci polkduran

  6. #6
    Membre éclairé Avatar de ppphil
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Juin 2007
    Messages : 612
    Points : 685
    Points
    685
    Par défaut
    Ouais, ben ça ne joue pas... Faux espoir....
    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
    private void AddToEntityCollection<T>(object objA, T objB, string propName)
        {
          PropertyInfo pi = objA.GetType().GetProperty(propName);
          ((ICollection<T>)pi.GetValue(objA, null)).Add(objB);
        }
     
        private void button5_Click(object sender, EventArgs e)
        {
          EntityObject obj1;
          EntityObject obj2;
          string listName;
     
          //Obtention de obj1 et obj2 et listName par désérialisation
     
          AddObjectToEntityCollection(obj, obj1, listName);
     
        }
    En fait, je ne connais que le type parent des deux objets soit EntityObject.
    Je les obtient par désérialisation.
    et la ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ((ICollection<T>)pi.GetValue(objA, null)).Add(objB);
    geule au p'tits pois comme quoi
    Impossible d'effectuer un cast d'un objet de type 'System.Data.Objects.DataClasses.EntityCollection`1[WindowsFormsApplication10.Obj1]' en type 'System.Collections.Generic.ICollection`1[System.Data.Objects.DataClasses.EntityObject]'.

  7. #7
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 082
    Points
    8 082
    Par défaut
    Euh c'est dangereux ce que tu fais la!
    Il n'y a aucune relation d'héritage entre Obj1 et Obj2, le cast est plutot hasardeux!

  8. #8
    Membre éclairé Avatar de ppphil
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Juin 2007
    Messages : 612
    Points : 685
    Points
    685
    Par défaut
    Oui, excuse-moi, je me suis un peu mélangé les pinceaux.
    Revoici la définition des class
    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
     
      public class Obj1 : EntityObject
      {
        public string Name;
      }
     
      public class Obj2 : EntityObject
      {
        public string Name;
      }
     
      public class ObjetA1 : EntityObject
      {
        public EntityCollection<Obj1> LstO1 { get; set; }
     
        public ObjetA1()
        {
          LstO1 = new EntityCollection<Obj1>();
        }
      }
     
      public class ObjetA2 : EntityObject
      {
        public EntityCollection<Obj2> LstO2 { get; set; }
     
        public ObjetA2()
        {
          LstO2 = new EntityCollection<Obj2>();
        }
      }
    et les fonctions
    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
     
         private void AddToEntityCollection<T>(object objA, T objB, string propName)
        {
          PropertyInfo pi = objA.GetType().GetProperty(propName);
          ((ICollection<T>)pi.GetValue(objA, null)).Add(objB);
        }
     
        private void button5_Click(object sender, EventArgs e)
        {
          EntityObject obj1;
          EntityObject obj2;
          string listName;
     
          //Obtention de obj1 et obj2 et listName par désérialisation
     
          AddObjectToEntityCollection(obj1, obj2, listName);
     
        }
    Et ce que je ne comprends pas c'est ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
         private void AddToEntityCollection<T>(EntityObject objA, T objB, string propName)
        {
          MessageBox.Show(typeof(T).ToString());
          //Affiche System.Data.Objects.DataClasses.EntityObject
          MessageBox.Show(objB.GetType().ToString());
          //Affiche MonApplication.Obj1
          PropertyInfo pi = objA.GetType().GetProperty(propName);
          ((ICollection<T>)pi.GetValue(objA, null)).Add(objB);
        }

  9. #9
    Membre émérite Avatar de Guulh
    Homme Profil pro
    Inscrit en
    Septembre 2007
    Messages
    2 160
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Points : 2 925
    Points
    2 925
    Par défaut
    Tu appelles AddObjectToEntityCollection sans spécifier explicitement le type <T>, ce qui est normal puisque tu ne le connais pas statiquement. Je suppose que ton désérializer va instancier un objet héritant de EntityObject, que tu vas coller dans obj2 après.

    Donc dans le contexte de ta méthode générique, T = EntityObject. La propriété que tu récupères par réflexion est un EntityCollection<Obj2>, qui n'implémente pas ICollection<EntityObject>, ICollection n'étant pas (à ma connaissance) ni covariant ni contravariant. Ca explique pourquoi ton cast lève une exception.
    Donc je ne pense pas que tu puisses avoir une référence typée vers la collection à laquelle tu veux rajouter un item. Puisque tu bosses déjà par réflexion, le plus simple est à mon avis d'appeler la méthode "Add" par réflexion. En pseudo-code (j'ai la flemme de vérifier) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    PropertyInfo pi = objA.GetType().GetProperty(propName);
    object laCollection = pi.GetValue(objA, null));
    MethodInfo addMethod = laCollection.GetType().GetMethod("Add");
    addMethod.Invoke(objB);
    Pour être bien précis : les génériques n'ont pas un comportement dynamique. C'est à dire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class Mere { }
    class Fille  : Mere { }
    public void F<T>() { }
    Mere m = new Fille();
    F(m); // équivaut à F<Mere>(m), pas à F<Fille>

  10. #10
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 082
    Points
    8 082
    Par défaut
    Désolé d'être désagréable mais je comprends rien à ta bidouille
    Quel est le besoin derrière ?

    (En reflechissant un peu je crois que c'est un problème de co ou contra-variance, meme si Obj1 est un EntityObject il n'est pas possible de dire que ICollection<Obj1> est une ICollection<EntityObject> sur le framework 3.5, ca existe à partir du 4)

    Edit: Han grillé par guulh

    Edit2: Voici les covariances et contravariances implémentées en .Net
    IEnumerable<T> (covariant)
    IEnumerator<T> (covariant)
    IQueryable<T> (covariant)
    IGrouping<TKey, TElement> (covariant)
    IComparer<T> (contravariant)
    IEqualityComparer<T> (contravariant)
    IComparable<T> (contravariant)
    Action<T> et ses frères et soeurs (contravariants)
    Func<TResult> et Func<T, TResult> (TResult est covariant; T,T1,T2 sont contravariants)
    Predicate<T> (contravariant)
    Comparison<T> (contravariant)
    Converter<TInput, TOutput> (TInput est contravariant; TOutput est covariant.)
    Source

  11. #11
    Membre émérite Avatar de Guulh
    Homme Profil pro
    Inscrit en
    Septembre 2007
    Messages
    2 160
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2007
    Messages : 2 160
    Points : 2 925
    Points
    2 925
    Par défaut
    Citation Envoyé par PitMaverick78 Voir le message
    Voici les covariances et contravariances implémentées en .Net
    Merci il me semblait bien à vu de nez qu'il y a des choses qu'on peut faire à une collection de filles mais pas à une collection de mères et réciproquement (toute grivoiserie mise à part ), ce qui explique l'absence de covariance et contravariance.

Discussions similaires

  1. [PRBL]Caste une liste d'une liste d'objet
    Par stephane92400 dans le forum Langage
    Réponses: 4
    Dernier message: 07/08/2007, 21h01
  2. yield return et liste générique ?
    Par Thomas Lebrun dans le forum C#
    Réponses: 4
    Dernier message: 18/07/2007, 13h16
  3. Sélection d'un champ d'une liste générique ?
    Par chuiben dans le forum Ada
    Réponses: 2
    Dernier message: 22/03/2007, 14h36
  4. Cast de List
    Par beebop dans le forum C#
    Réponses: 4
    Dernier message: 11/01/2007, 09h06
  5. Réponses: 12
    Dernier message: 23/09/2006, 12h12

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