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 :

Dictionary: reference ou copy ?


Sujet :

C#

  1. #1
    Invité
    Invité(e)
    Par défaut Dictionary: reference ou copy ?
    Bonjour à toutes/tous,

    J'aimerais savoir si l'ajout d'un élément à un dictionary se fait via référence ou via copy. En effet, j'ai une classe contenant un dictionnaire d'éléments et lors d'un ajout j'aimerai pouvoir retourner le dernier élément ajouté, le modifier, et être sûr que le dictionnaire contienne bien la référence de cet objet (et donc, qu'il reflète l'état actuel de l'objet).

    Des informations ? Ce qui me fait particulièrement hésiter c'est la doc MSDN de la méthode Add:
    If the Count property value already equals the capacity, the capacity of the Dictionary<TKey, TValue> is increased by automatically reallocating the internal array, and the existing elements are copied to the new array before the new element is added.
    Merci d'avance

  2. #2
    Membre éprouvé

    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juin 2011
    Messages
    487
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine et Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2011
    Messages : 487
    Points : 945
    Points
    945
    Par défaut
    Bonjour,

    En fait, les collections contiennent des tableaux "cachés". Ces tableaux ont une capacité (propriété Capacity), qui sont les limites du tableau et un nombre d'élements (propriété Count).

    Ce qu'explique msdn ici, c'est que lorsque tu ajoutes un objet qui va venir compléter le tableau, la collection recrée un autre tableau plus grand et copie tous les éléments dedans.

    De cette manière, point de vue utilisateur on a l'impression d'avoir un tableau infini. C'est juste qu'un tableau plus grand est recrée à chaque fois que celui en cours est plein.

    Je reviens à ton soucis initial : Les objets ajoutés dans un dico sont bien passés par référence. Tu peux donc modifier ton objet depuis un autre pointeur, il sera mis à jour dans le dico.

  3. #3
    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 : 43
    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 753
    Points
    39 753
    Par défaut
    Citation Envoyé par NaeiKinDus Voir le message
    J'aimerais savoir si l'ajout d'un élément à un dictionary se fait via référence ou via copy. En effet, j'ai une classe contenant un dictionnaire d'éléments et lors d'un ajout j'aimerai pouvoir retourner le dernier élément ajouté, le modifier, et être sûr que le dictionnaire contienne bien la référence de cet objet (et donc, qu'il reflète l'état actuel de l'objet).
    Ca dépend si c'est des types valeur (= struct, par exemple int, bool, DateTime...) ou des types référence (= class, par exemple string, object, Form...).

    Les types valeur sont copiés par valeur : c'est l'objet lui-même qui est copié. Mais de toutes façons ils sont généralement "immutables" (on ne peut pas les modifier), donc la question ne se pose pas vraiment dans ton cas. Il est d'ailleurs fortement déconseillé de créer des types valeur mutables.

    Les types référence sont copiés par référence : ce n'est pas l'objet lui-même qui est copié, mais une référence vers cet objet. Donc quand tu obtiens l'objet à partir du dictionnaire, tu récupères une autre référence vers le même objet, les modifications se retrouvent donc bien dans le dictionnaire.

    Citation Envoyé par NaeiKinDus Voir le message
    Des informations ? Ce qui me fait particulièrement hésiter c'est la doc MSDN de la méthode Add:
    Ca n'a pas vraiment de rapport, il s'agit juste de la façon dont le dictionnaire s'agrandit selon les besoins. La copie mentionnée dans la doc suit les mêmes règles que j'ai expliquées ci-dessus. Mais de toutes façons tu n'a pas à t'en préoccuper, c'est juste le fonctionnement interne du dictionnaire et ça n'a pas d'impact sur la façon de l'utiliser (du moins dans le cadre de ta question)

  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 : 43
    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 753
    Points
    39 753
    Par défaut
    Citation Envoyé par MaximePalmisano Voir le message
    Je reviens à ton soucis initial : Les objets ajoutés dans un dico sont bien passés par référence. Tu peux donc modifier ton objet depuis un autre pointeur, il sera mis à jour dans le dico.
    Seulement pour les types référence... Par exemple dans un Dictionary<string, int>, le int est passé par valeur, pas par référence. Mais de toutes façons un int n'est pas modifiable, donc ce n'est pas un problème. Mais si tu prends un Dictionary<string, Point> par exemple (Point est un type valeur), attention ! Exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Point p1 = new Point(10, 20);
    dict.Add("toto", p1);
    Point p2 = dict["toto"];
    p2.X = 42;
    p2.Y = 99;
    Dans ce cas le Point dans le dictionnaire n'est pas modifié, car on n'a modifié qu'une copie !

  5. #5
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 177
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 177
    Points : 25 125
    Points
    25 125
    Par défaut
    je complèterais MaximePalmisano en disant que quand msdn dit que le tableau est copié dans un autre lors de l'agrandissement, si tu as mis des types par références, ce sont les pointeurs qui sont copiées (4 ou 8 octets, donc pas l'objet pointé)
    donc pas de soucis


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Button monBouton = New Button
    monButton est un pointeur de 8 octets (en x64) qui pointe vers l'espace mémoire contenant le New Button (le pointeur contient null si pas d'initialisation)
    dans le dictionnaire c'est juste le pointeur vers l'espace mémoire qui sera enregistré

    i contient directement la valeur, pas de pointeur vers un espace mémoire externe
    quand on le met dans un dictionnaire, la valeur est copiée, la modifier de peux pas modifier i

  6. #6
    Invité
    Invité(e)
    Par défaut
    Merci à vous deux pour vos réponses, ça éclairci déjà plusieurs points.
    Pour précision, le doute que j'avais quant à la doc MSDN portait juste sur le type de réallocation de tableau (s'ils font une copy type Clone, il n'y a plus d'histoire de référence et donc cet classe ne fait pas ce que je souhaite; s'ils font une copy en utilisant la référence, je peux potentiellement l'utiliser).

    Par contre Tomlev, ton deuxième message est en gros ce que je souhaite faire (et que je ne peux à priori pas d'après ce que tu me donnes). Ça donnerait quelque chose dans ce genre:
    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
     
    class UneClass
    {
      private Dictionary<String, MaClass> dict = new Dictionary<String, MaClass>();
     
      public MaClass SpawnObject()
      {
        MaClass A = new MaClass('toto', 'tata');
        dict.Add('key', A);
        return A;
      }
    }
     
    UneClass tutu = new UneClass();
    MaClass titi = tutu.SpawnObject();
    titi.UneFonction();
    titi.Propriete = '15';
     
    // A ce niveau, la propriété de titi vaudrait 15, et si on accède au dictionnaire utilisé par "UneClasse" et qu'on cherche l'objet 'toto', sa propriété faut aussi 15 (si on peut jouer tout le code en référence, donc).
    La finalité de tout ça serait d'avoir un objet "maître" qui contient une liste de sous-éléments que l'on ajoute via une méthode Spawn qui retourne la référence de l'objet. Cet objet retourné contient, comme l'objet maître, une liste de sous-éléments avec laquelle on peut interagir de la même manière qu'avec l'objet maître. En gros, ça donnerait vaguement un arbre.

    Donc y'a-t'il un moyen de faire ça de la manière que je propose, ou je me goure complètement ? Ou bien y'a-t'il une solution plus "propre" et/ou simple ?

    Merci à tous

  7. #7
    Membre éprouvé

    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juin 2011
    Messages
    487
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine et Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2011
    Messages : 487
    Points : 945
    Points
    945
    Par défaut
    Bien sur, quand je disais copié par référence, j'avais en tête les objets de type référence :p Désolé pour l'oubli !

  8. #8
    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 : 43
    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 753
    Points
    39 753
    Par défaut
    Citation Envoyé par NaeiKinDus Voir le message
    Par contre Tomlev, ton deuxième message est en gros ce que je souhaite faire (et que je ne peux à priori pas d'après ce que tu me donnes)
    Bah si tu peux, si MaClass est une classe (type référence) et non une structure (type valeur)

  9. #9
    Invité
    Invité(e)
    Par défaut
    Au temps pour moi tomlev, j'ai mal lu ton dernier message (ou bien est-ce que tu l'as édité ?).
    J'avais lu que ta nouvelle classe P2 était en fait P1...
    Mais du coup, comment faire que sur la ligne suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Point p2 = dict["toto"];
    p2 récupère une référence et non une copie ? (à priori je m'en fiche car l'instance de base ajoutée au dictionnaire est retournée par ma fonction, mais on ne sait jamais...).

  10. #10
    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 : 43
    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 753
    Points
    39 753
    Par défaut
    Citation Envoyé par NaeiKinDus Voir le message
    (ou bien est-ce que tu l'as édité ?).
    J'ai remplacé p1 par p2 pour la 2e variable, c'était une erreur dans le message initial. Mais ça change pas grand chose au final...


    Citation Envoyé par NaeiKinDus Voir le message
    Mais du coup, comment faire que sur la ligne suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Point p2 = dict["toto"];
    p2 récupère une référence et non une copie ? (à priori je m'en fiche car l'instance de base ajoutée au dictionnaire est retournée par ma fonction, mais on ne sait jamais...).
    Je te l'ai déjà dit : pour un type référence, c'est bien une référence vers le même objet que tu récupères. Mais dans le cas de Point, qui est un type valeur, tu récupères une copie. Il n'est pas possible de récupérer une référence dans ce cas (mais tu peux toujours remettre l'objet dans le dictionnaire après l'avoir modifié)

  11. #11
    Inactif  
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Janvier 2007
    Messages
    6 604
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Janvier 2007
    Messages : 6 604
    Points : 13 317
    Points
    13 317
    Par défaut
    Citation Envoyé par NaeiKinDus Voir le message
    p2 récupère une référence et non une copie ? (à priori je m'en fiche car l'instance de base ajoutée au dictionnaire est retournée par ma fonction, mais on ne sait jamais...).
    Non, car Point est une structure pas une classe; c'est un type "valeur", pas "référence" comme cela t'a été expliqué plus haut.

    Tomlev a pris cela comme "contre-exemple" car Point est justement un type valeur non immutable, alors que la pluspart sont immutables.

  12. #12
    Invité
    Invité(e)
    Par défaut
    OK parfait merci à tous pour les clarifications ! Je vais faire ça comme ça.

  13. #13
    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 : 43
    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 753
    Points
    39 753
    Par défaut
    Citation Envoyé par Bluedeep Voir le message
    car Point est justement un type valeur non immutable, alors que la pluspart sont immutables.
    Oui, j'aurais dû préciser ça, ça aurait été plus clair...

    D'ailleurs j'aimerais bien savoir pourquoi ils ont rendu cette structure mutable
    Sans doute parce qu'ils l'utilisent directement pour l'interop avec GDI+, mais dans ce cas ils auraient au moins pu laisser les propriétés X et Y en read-only

  14. #14
    Inactif  
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Janvier 2007
    Messages
    6 604
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Janvier 2007
    Messages : 6 604
    Points : 13 317
    Points
    13 317
    Par défaut
    Citation Envoyé par tomlev Voir le message
    Oui, j'aurais dû préciser ça, ça aurait été plus clair...

    D'ailleurs j'aimerais bien savoir pourquoi ils ont rendu cette structure mutable
    Sans doute parce qu'ils l'utilisent directement pour l'interop avec GDI+, mais dans ce cas ils auraient au moins pu laisser les propriétés X et Y en read-only
    Il y a peut être une autre explication :

    si tu mets les propriétés x et y (dans le cas de Point) en readonly, tu ne peux construire le Point qu'à partir d'un constructeur à paramètres; ce qui empêche une sérialisation SOAP (et Xml en général)

  15. #15
    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 : 43
    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 753
    Points
    39 753
    Par défaut
    Citation Envoyé par Bluedeep Voir le message
    si tu mets les propriétés x et y (dans le cas de Point) en readonly, tu ne peux construire le Point qu'à partir d'un constructeur à paramètres; ce qui empêche une sérialisation SOAP (et Xml en général)
    Possible, mais en pratique c'est pas le genre de choses qu'on transmet fréquemment à un webservice... Je penche quand même plus pour l'argument de l'interop GDI+

  16. #16
    Inactif  
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Janvier 2007
    Messages
    6 604
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Janvier 2007
    Messages : 6 604
    Points : 13 317
    Points
    13 317
    Par défaut
    Citation Envoyé par tomlev Voir le message
    Possible, mais en pratique c'est pas le genre de choses qu'on transmet fréquemment à un webservice... Je penche quand même plus pour l'argument de l'interop GDI+
    Je suis d'accord avec toi pour le webservice, mais persister en XML des coordonnées de fenêtre par exemple n'a rien d'exceptionnel (persistance des choix utilisateurs, etc .....)

  17. #17
    Inactif  
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Janvier 2007
    Messages
    6 604
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France

    Informations professionnelles :
    Activité : Chef de projet NTIC

    Informations forums :
    Inscription : Janvier 2007
    Messages : 6 604
    Points : 13 317
    Points
    13 317
    Par défaut
    Citation Envoyé par MaximePalmisano Voir le message
    Bonjour,

    En fait, les collections contiennent des tableaux "cachés". Ces tableaux ont une capacité (propriété Capacity), qui sont les limites du tableau et un nombre d'élements (propriété Count).
    Jusque là, c'est vrai.

    Ce qu'explique msdn ici, c'est que lorsque tu ajoutes un objet qui va venir compléter le tableau, la collection recrée un autre tableau plus grand et copie tous les éléments dedans.
    Ca aussi.

    Je reviens à ton soucis initial : Les objets ajoutés dans un dico sont bien passés par référence. Tu peux donc modifier ton objet depuis un autre pointeur, il sera mis à jour dans le dico.
    Là, c'est un peu n'importe quoi. Si tu ajoutes des éléments de type value dans un dico, ils vont être passés par référence ? intéressant.

  18. #18
    Membre éprouvé

    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juin 2011
    Messages
    487
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine et Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2011
    Messages : 487
    Points : 945
    Points
    945
    Par défaut
    C'est pour ça que j'ai mis un deuxième message en erratum car en tapant le premier je me suis mal relu et j'ai oublié :

    Les objets de type référence ajoutés dans un dico sont bien passés par référence
    Donc avant de troller ça serait cool de lire le sujet dans sa totalité

  19. #19
    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 : 43
    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 753
    Points
    39 753
    Par défaut
    Citation Envoyé par MaximePalmisano Voir le message
    C'est pour ça que j'ai mis un deuxième message en erratum car en tapant le premier je me suis mal relu et j'ai oublié :
    La prochaine fois, utilise le bouton "Éditer", ça évitera les malentendus

    Par contre, attention à l'amalgame entre "passage par référence" et "type référence"...

    Les objets de type référence ne sont pas vraiment "passés par référence" : c'est une référence qui est passée par valeur ; la méthode appelée ne peut pas changer la référence pour la faire pointer vers un autre objet (ce qui serait le cas avec un passage par référence)

    EDIT: Un petit exemple pour clarifier

    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
    class Program
    {
        static void Main()
        {
            Foo f = new Foo { Bar = 42 };
            ByValue(f);
            Console.WriteLine(f.Bar); // Toujours 42
     
            ByReference(ref f);
            Console.WriteLine(f.Bar); // 99 (la référence a été modifiée)
        }
    }
     
     
    class Foo
    {
        public int Bar { get; set; }
    }
     
    void ByValue(Foo f)
    {
        f = new Foo { 99 };
    }
     
    void ByReference(ref Foo f)
    {
        f = new Foo { 99 };
    }

  20. #20
    Membre éprouvé

    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juin 2011
    Messages
    487
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine et Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2011
    Messages : 487
    Points : 945
    Points
    945
    Par défaut
    Exact, je vais éditer de suite pour éviter les futurs malentendus.

    D'ailleurs je ne savais pas qu'il était possible en .NET managé de changer la référence au sein d'une méthode comme on pourrait le faire en C++.

    On en apprend tous les jours

    EDIT : Je peux plus éditer mon message, peut être parce qu'il est trop vieux

    EDIT2 : J'ai pas l'impression que ce dont je parle dans mon message soit possible car msdn parle également de passage par référence http://msdn.microsoft.com/fr-fr/libr...(v=vs.80).aspx

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Réponses: 4
    Dernier message: 11/09/2012, 12h20
  2. Réponses: 20
    Dernier message: 05/09/2011, 06h59
  3. Reference plutot que copie
    Par LeonardL dans le forum C#
    Réponses: 1
    Dernier message: 05/12/2008, 12h12
  4. des references et des copies en Python ?
    Par KINENVEU dans le forum Général Python
    Réponses: 3
    Dernier message: 07/06/2007, 22h37
  5. Peux t'on créer une copie locale de l'objet partagé?
    Par Anonymous dans le forum CORBA
    Réponses: 8
    Dernier message: 16/04/2002, 16h20

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