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 :

Passage d'objet par valeur


Sujet :

C#

  1. #1
    Modérateur
    Avatar de roro06
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    1 480
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 1 480
    Points : 1 978
    Points
    1 978
    Par défaut Passage d'objet par valeur
    Bonjour

    Je définis deux classes comme 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
        public class Populace
        {
            public Populace()
            {
                E = new enfants();
            }
            public enfants E { get; set; }
            private int ageMajorite;
     
            public int AgeMajorite
            {
                get { return ageMajorite; }
                set
                {
                    ageMajorite = value;
                    if (E.Age < ageMajorite)
                        E.majeur = false;
                    else
                        E.majeur = true;
                }
            }
        }
     
        public class enfants
        {
            public enfants(){}
            public enfants(int age) { }
     
            public int Age { get; set; }
            public bool majeur { get; set; }
     
        }
    Dans mon programme, je fais ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
      Populace p1 = new Populace();
                Populace p2 = new Populace();
                enfants e = new enfants(19);
                e.majeur = true;
     
                p1.E = e;
                p2.E = e;
     
                p1.AgeMajorite = 18;
                p2.AgeMajorite = 21;
    Je m'apercois que la valeur de e.majeur est modifiée, alors que j'aurais voulu que seule celle de p2.E.majeur le soit. En clair, je pensais que lorseque je fais e était passé par valeur, or il semblerait qu'il soit passé par référence. Comment fait-on pour faire un passage d'objet par valeur ?

    Merci d'avance

  2. #2
    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 roro06 Voir le message
    En clair, je pensais que lorseque je fais e était passé par valeur, or il semblerait qu'il soit passé par référence. Comment fait-on pour faire un passage d'objet par valeur ?

    Merci d'avance
    Contrairement à C++ par exemple, pour lequel tout type peut être passé par valeur ou par référence (avec les & et *), en C#, c'est le concepteur de l'objet qui détermine son usage, en indiquant si c'est une struct ou une class.

    Ici, enfants est une classe, et une classe n'est jamais passée par valeur (c'est à dire dupliquée) implicitement. Contrairement aux structs (telles que int).
    Donc si tu ne veux pas que p1.e et p2.e ne référencent le même objet, tu as deux choix : ou faire d'enfants une struct (sachant que dans le pratique, très peu d'objets ont intérêt à être des structs, car très peu d'objets doivent être systématiquement passés par valeur), ou rendre ta classe clonable, ce qui consiste juste à lui ajouter une méthode "public enfants Clone() { ... }" (à toi de remplir les ...) que tu appelleras explicitement après.

    Je t'inivte aussi à te renseigner sur le mot-clé "ref", qui permet de passer par valeur référence (comme son nom l'indique) une classe en paramètre d'une méthode.

  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
    Citation Envoyé par Guulh Voir le message
    Je t'inivte aussi à te renseigner sur le mot-clé "ref", qui permet de passer par valeur une classe en paramètre d'une méthode.
    Tu as du fourcher ou je me trompe...
    msdn :Le mot clé ref fait en sorte que les arguments soient passés par référence. La conséquence est que toute modification apportée au paramètre dans la méthode est reflétée dans cette variable lorsque la méthode appelante récupère le contrôle.

  4. #4
    Modérateur
    Avatar de roro06
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    1 480
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 1 480
    Points : 1 978
    Points
    1 978
    Par défaut
    Merci de ta réponse (ultra-rapide ). j'étais effectivement en train d'éplucher ce tuto qui semble aller dans le même sens.

    Je vais tenter de rendre ma classe Clonable et donc de "remplir les ...", sachant que dans mon projet, la classe utilisée est plus complexe que la classe enfants présentée ici. S'agit-il "simplement" d'effectuer un recopie des champs (public et privés) de la classe à rendre Clonable ? Je suppose que si tel est le cas, les collections doivent être reproduites aussi, comme présenté dans cette discussion ?

  5. #5
    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 ppphil Voir le message
    Tu as du fourcher ou je me trompe...
    En effet , j'édite mon message pour éviter d'induire en erreur la postérité
    Citation Envoyé par roro06 Voir le message
    Merci de ta réponse (ultra-rapide ). j'étais effectivement en train d'éplucher ce tuto qui semble aller dans le même sens.

    Je vais tenter de rendre ma classe Clonable et donc de "remplir les ...",
    Pour une shallow copy, la méthode privée MemberwiseClone suffit. Sinon, oui, y'a du code de plomberie.
    Mais en 4 ans de .Net, je n'ai rencontré ce besoin que très peu souvent. Note façon de concevoir est probablement déformée par l'outil que l'on utilise le plus, mais dans mon esprit, les besoins de copie par valeur (et donc de duplication) sont très rares. Chaque "populace" ici a son propre "enfants" (et qu'il détient vraiment, puisqu'il se permet de modifier ses propriétés). On a donc besoin de deux "enfants", créés séparément. Ton exemple doit être réducteur pour faire ressortir ton souci, mais par exemple, j'aurais plutôt fait une référence de "enfants" vers populace, puisque la majorité de enfants dépend d'une propriété de populace, et enfants aurait donc quelque chose du style
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    public bool IsMajeur { get { return this.Age >= this.populace.AgeMajorité; } }

  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
    Regardes déjà ce que peux te faire la méthode MemberwiseClone()

  7. #7
    Modérateur
    Avatar de roro06
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    1 480
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 1 480
    Points : 1 978
    Points
    1 978
    Par défaut
    Ton exemple doit être réducteur pour faire ressortir ton souci
    Oui.

    Voici un code plus représentatif de mon problème :
    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
     
      public class Populace
        {
            public Populace()
            {
                gens = new List<personne>();
            }
     
            private List<personne> gens;
     
            public List<personne> Gens
            {
                get { return gens; }
                set
                {
                    List<personne> copie = new List<personne>();
                    copie.AddRange(value);
                    gens = copie;
                }
            }
     
            private int ageMajorite;
     
            public int AgeMajorite
            {
                get { return ageMajorite; }
                set
                {
                    ageMajorite = value;
                    foreach (personne p in gens)
                    {
                        if (p.Age < ageMajorite)
                            p.majeur = false;
                        else
                            p.majeur = true;
                    }
                }
            }
        }
     
        public class personne
        {
            public personne() { }
            public personne(int age)
            {
                this.Age = age;
            }
     
            public int Age { get; set; }
            public bool majeur { get; set; }
        }
    (j'ai renommé enfants en personne)

    et mon code principal :
    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
     
     Populace p1 = new Populace();
                Populace p2 = new Populace();
                personne e = new personne(19);
                e.majeur = true;
     
                List<personne> echantillon = new List<personne>();
                echantillon.Add(e);
     
                p1.Gens = echantillon;
                p1.AgeMajorite = 18;
                Response.Write(string.Format("majeur : {0}<br/>", e.majeur));
     
                p2.Gens = echantillon;
                p2.AgeMajorite = 21;
                Response.Write(string.Format("majeur : {0}<br/>", e.majeur));
            }
    la valeur de e.majeur est systématiquement modifiée par p2.AgeMajorite, alors que j'ai besoin qu'elle soit inchangée. Pire, si je teste p1.Gens[0].majeur, la valeur retournée est false . En fait, je peux avoir besoin, après coup, de tester des valeurs px.gens[i].majeur, en étant sûr qu'elles n'ont pas été modifiée entre temps.

    j'ai également essayé avec MemberwiseClone(), mais je crois que dans mon cas précis, il faut que je fasse une copie "profonde", mais je ne m'en sors pas

  8. #8
    Expert éminent
    Avatar de StormimOn
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2005
    Messages
    2 593
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Sarthe (Pays de la Loire)

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

    Informations forums :
    Inscription : Mai 2005
    Messages : 2 593
    Points : 7 660
    Points
    7 660
    Par défaut
    Si besoin d'une copie en profondeur, on peut utiliser la sérialisation binaire pour le faire de manière très simple. La seule obligation est d'ajouter l'attribut Serializable sur les classes du graphe d'objets.

    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
    using System;
    using System.IO;
    using System.Runtime.Serialization.Formatters.Binary;
     
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                MaClasse maClasse = new MaClasse() { Valeur = "test", MaClasseEnfant = new MaClasseEnfant() { Valeur = "autre test" } };
     
                MaClasse clone = maClasse.Clone();
            }
        }
     
        [Serializable]
        public class MaClasse
        {
            public string Valeur { get; set; }
            public MaClasseEnfant MaClasseEnfant { get; set; }
     
            public MaClasse Clone()
            {
                byte[] data = null;
                using (MemoryStream ms = new MemoryStream())
                {
                    new BinaryFormatter().Serialize(ms, this);
                    data = ms.ToArray();
                }
     
                using (MemoryStream ms = new MemoryStream(data))
                {
                    return (MaClasse)new BinaryFormatter().Deserialize(ms);
                }
            }
        }
     
        [Serializable]
        public class MaClasseEnfant
        {
            public string Valeur { get; set; }
        }
    }

  9. #9
    Invité
    Invité(e)
    Par défaut
    Cool, c'est vrai que la sérialisation binaire facilite le clonage en profondeur.
    une méthode de plus dans mes notes personnelles.
    merci.

  10. #10
    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
    Si tu veux que e ne soit pas modifié par le tripatouillage de p1 et p2, il ne faut pas le leur donner, mais un clone.

    Donc
    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
                Populace p1 = new Populace();
                Populace p2 = new Populace();
                personne e = new personne(19);
                e.majeur = true;
                List<personne> echantillon = new List<personne>();
                echantillon.Add(e);
                List<personne> echantillon1 = SerializerDeStormimOn.DeepCopy(echantillon);
                List<personne> echantillon2 = SerializerDeStormimOn.DeepCopy(echantillon);           
     
                p1.Gens = echantillon1;
                p1.AgeMajorite = 18;
                Response.Write(string.Format("majeur : {0}<br/>", e.majeur));
     
                p2.Gens = echantillon2;
                p2.AgeMajorite = 21;
                Response.Write(string.Format("majeur : {0}<br/>", e.majeur));
    Mais le plus simple est de revoir la conception pour ne pas avoir besoin de faire ça. Les relations N - 1 sont rares ; pour reprendre les termes de ton exemple, une même personne est unique, induplicable, et ne peut appartenir à deux populaces.

Discussions similaires

  1. Passage d'objet par valeur
    Par Ghurdyl dans le forum C++/CLI
    Réponses: 4
    Dernier message: 15/07/2009, 18h08
  2. Passage des arguments par valeur
    Par mpereg dans le forum Général Python
    Réponses: 4
    Dernier message: 13/03/2007, 18h12
  3. [POO] Objet par valeur
    Par seb34 dans le forum Général JavaScript
    Réponses: 3
    Dernier message: 03/08/2006, 23h49
  4. [String] passage d'objet par référence
    Par adilou1981 dans le forum Langage
    Réponses: 8
    Dernier message: 01/04/2005, 16h22
  5. [ JSP ][ Débutant ] Passage d'objet par un forward
    Par captainpouet dans le forum Servlets/JSP
    Réponses: 5
    Dernier message: 08/04/2004, 11h33

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