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 :

Différences class/struct C# [Débutant]


Sujet :

C#

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    31
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 31
    Par défaut Différences class/struct C#
    Bonjour. De provenance C++, je suis manifestement un peu à la peine dans la manipulation des concepts C# (d'objets Struct et d'objets class en C#), le plus simple, je me permets de vous mettre mon code puis je vous explique :
    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
    public class forum
    {
        //déclarations (variables, structure Struct Result, etc, déclaration consturcteurs
        //...    
        //méthodes
        static public void doWork()
        {
            double[,] tmpRes = new double[m, 2]; //stockage temporaire des données
            int n = 10, m = 100;
            myResults = new List<Result>(m);
            for (int i = 0; i < m; ++i) myResults.Add(new Result(n)); //initialisation de la liste
            Result myResult__ = new Result(n);//déclaration de la variable locale temporaire nécessaire pour l'affectation des données struct et éviter erreur CS1612
            double x__, y__;
            Hashtable metaData = new Hashtable();
     
            foreach (var kvp in myKeyValuePair)
            {
                Result myResults__ = new Result(n);//déclaration de la variable locale temporaire nécessaire pour l'affectation des données struct 
                for (int i = 0; i < _nbSimulations; ++i)
                {
                    for (int j = 0; j < m; ++j)
                    {
                        x__ = uneFonction();
                        y__ = autreFonction();
                        tmpRes[j, 0] += x__;
                        tmpRes[j, 1] += y__;
                    }
                }
                for (int j = 0; j < m; ++j)
                {
                    tmpRes[j, 0] *= tmpRes[j, 1];
                    tmpRes[j, 1] /= tmpRes[j, 0];
                    myResults__.x = tmpRes[j, 0];
                    myResults__.y = tmpRes[j, 1];
                    myResults[j] = myResult__;
                }
                metaData.Add(kvp.idx(), myResults);
            }
            return;
        }
    }
    Mes questions :

    1. Pourquoi ne m'a ti-l pas été permis par le compilo (le type Result est vous l'aurez compris une "struct"), dans mes boucles, d'affecter directement mes valeurs calculées à la variable : myResults.x (ou myResult.y) ? J'ai pour contourner, dû storer mes valeurs dans les membres d'une struct intermédiaire avant de charger myresults.

    2. Plus gênant, dans le code ci-dessus où je dois ranger mes résultats dans une "List<Result> metaData" je me retrouve à la fin de mes boucles, toujours la même valeur pour toutes mes clés de List, un peu comme ci les éléments de la listes avaient tous pointés sur la même adresse mémoire, laquelle adresse mémoire contient naturellement en fin de boucle, les valeurs de myresults__ telles qu'à la dernière itération dans la boucle.

    Je ne comprends vraiment pas ce qui cloche dans ce code ni ce que je dois comprendre. Ce que je sais c'est que je m'arrache les cheveux. Merci donc pour le secours que vous pourrez m'apporter.

  2. #2
    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
    Par défaut
    Quelle est la définition de Result?

  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
    Par défaut
    Citation Envoyé par simong Voir le message
    1. Pourquoi ne m'a ti-l pas été permis par le compilo (le type Result est vous l'aurez compris une "struct"), dans mes boucles, d'affecter directement mes valeurs calculées à la variable : myResults.x (ou myResult.y) ? J'ai pour contourner, dû storer mes valeurs dans les membres d'une struct intermédiaire avant de charger myresults.
    Une structure est un type valeur. Les types valeur, comme leur nom l'indique, sont manipulés par valeur, et non par référence : quand tu passes une structure en paramètre, ou que tu renvoies une structure à partir d'une méthode (ou d'une propriété ou d'un indexeur), c'est en réalité une copie qui est renvoyée.

    Si tu avais le droit de faire ça :

    myResults[j] renverrait une copie de l'objet qui est dans la liste, et le fait de modifier la valeur de x pour cet objet ne modifierait pas celui qui est dans la liste. L'instruction ne servirait donc à rien... On pourrait considérer qu'il est tout à fait légitime d'écrire une instruction qui ne sert à rien, mais en l'occurrence ça pourrait être une cause de bugs très difficiles à détecter, donc plutôt que de te laisser faire ça, le compilateur le détecte et l'interdit.

    Tu pourrais régler le problème comme ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    myResults[j] = new Result { x = tmpRes[j, 0], y = tmpRes[j, 1] };
    (en supposant que tu utilises .NET 3.5 ou plus)

    Citation Envoyé par simong Voir le message
    2. Plus gênant, dans le code ci-dessus où je dois ranger mes résultats dans une "List<Result> metaData" je me retrouve à la fin de mes boucles, toujours la même valeur pour toutes mes clés de List, un peu comme ci les éléments de la listes avaient tous pointés sur la même adresse mémoire, laquelle adresse mémoire contient naturellement en fin de boucle, les valeurs de myresults__ telles qu'à la dernière itération dans la boucle.
    Difficile de répondre avec seulement le code que tu as posté.
    D'ailleurs dans ton code, metaData est une Hashtable, pas une List<Result>...
    Et c'est quoi "myKeyValuePair" ? D'où ça vient, et c'est de quel type ?


    En ce qui concerne les types valeur et les types référence, si tu n'as pas peur de l'anglais je te suggère de lire ceci :
    http://www.albahari.com/valuevsreftypes.aspx
    http://www.yoda.arachsys.com/csharp/references.html
    C'est très important de bien comprendre ces notions si tu veux progresser

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    31
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 31
    Par défaut
    Merci pour vos retours.
    Pour reprendre les interrogations dans l'ordre, result est une structure je dirais "basique" qui contient deux membres x et y qui sont des double.
    metaData est une Hashtable et non une List, pardon je me suis emmêlé dans la copie et la mise en forme de mon cas.

    Enfin, le KeyValuePair stocke des objets de type class dans lequel je lis - notamment la property "name" dont je me sers pour fabriquer les Keys de mon Hashtable.

    Il ne manque pas grand chose de mon fichier source, simplement la déclaration des variables de la class forum (dont x et y sont) et la déclaration de la struct Result qui expose des variables x et y du même type - en cohérence à tout le moins) - que les variables de Forum.

    Lorsque j'exéc ute, avec un arrêt juste derrière la ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    metaData.Add(kvp.idx(), myResults);
    je remplis bien mon Hashtable avec une liste d'objets Result. :

    - Au premier passage, ma liste de Result contient m objets Result
    - Au deuxième passage, m nouveau Resul sont ajoutés, mais ils ont écrasés la précédente liste de valeurs
    - pareil aux passages suivants

    Si bien qu'au dernier passage, le Hashtable contient pour chacune des clés, une unique collection de valeurs (la dernière calculée par la boucle, je sais pas si c'est clair).

    C'est un peu comme si les valeurs stockées dans le Hashtable étaient une adresse sur l'objet Result.....

    J'espère que vous voyez un peu mieux....

  5. #5
    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
    Par défaut
    Ah ok, j'avais pas bien vu...

    En fait, tu ajoutes la même liste à chaque itération : List<T> est un type référence, donc il n'est pas copié quand tu le passe en paramètre, tu passes juste une référence vers la même instance. Il faut donc que tu recrées une nouvelle instance de List<Result> à chaque itération de la boucle foreach, sinon tu manipules toujours la meme liste

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    31
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 31
    Par défaut
    Ah oui, c'est cool ! Encapsulation de l'objet "struct Result" dans la "class List" crée donc une référence sur les objets de ma liste, merci!!

  7. #7
    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
    Par défaut
    Citation Envoyé par simong Voir le message
    Encapsulation de l'objet "struct Result" dans la "class List" crée donc une référence sur les objets de ma liste
    Euh... what ?
    List<Result> est une classe (type référence), mais Result reste une struct (type valeur)... il n'y a pas de "référence vers les objets de ta liste", seulement une référence vers la liste ; et la liste contient directement(*) les objets Result, pas des références

    (*) enfin pas tout à fait en vrai : en interne List<Result> utilise un tableau de Result

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    31
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 31
    Par défaut
    Oui, oui, référence sur la liste d'objets Result, bien sûr!! :-/

    Je continue de me casser le nez :
    La fin de mon code était tout simplement :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
                for (int j = 0; j < m; ++j)
                {
                    myResult__.x = ((List<Result>)globalResults["kvp1Name"])[j].x;
                    myResult__.y = ((List<Result>)globalResults["kvp2Name"])[j].y;
                    for (int l = 0; l < n; ++l)
                    {
                        myResult__.z[l] = ((List<Result>)globalResults["Upper_Spot_Bumped_Context"])[j].x;
                        myResult__.z[l] -= ((List<Result>)globalResults["Standard_Context"])[j].y;
                        myResult__.z[l] /= 2;
                    }
                    myResults[j] = myResult__;
                }
    Et là à nouveau, après la boucle, mon myResults n'est pas rempli des bonnes valeurs (tantôt valeurs qui se réécrasent, , tantôt valeurs correctes, tantôt des 0, donc a priori toujours un joli problème de mémoire!
    Je n'y comprends plus rien, même si juste avant ce bout de code je vais : je vais un nouveau new List<Result> pour myResults!!

    Y'a vraiment un truc que je ne comprends pas. Vous auriez pas une doc en français sur ces questions (suis pas trop fortiche en anglais) ?

  9. #9
    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
    Par défaut
    Euh, là je vois pas trop... tu as essayé de déboguer en pas à pas pour voir ce qui se passe ?

    Argh
    On n'appelle jamais une variable "l" (ou "O") ! Comment tu fais la différence avec "1" (ou "0") ? dans la plupart des polices utilisées pour le code, ces 2 caractères sont identiques ou au moins très ressemblants, c'est une horreur pour la lisibilité...

  10. #10
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    31
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 31
    Par défaut
    Citation Envoyé par tomlev Voir le message
    Euh, là je vois pas trop... tu as essayé de déboguer en pas à pas pour voir ce qui se passe ?

    Argh
    On n'appelle jamais une variable "l" (ou "O") ! Comment tu fais la différence avec "1" (ou "0") ? dans la plupart des polices utilisées pour le code, ces 2 caractères sont identiques ou au moins très ressemblants, c'est une horreur pour la lisibilité...
    Oui pas à pas absolument, et notamment lorsque je fais le myResults = new Result();, en testant ce que je considère être la référence de la variable "&myResults" dans le quick watch, j'ai strictement la même adresse avant d'avoir fait le new, qu'après. Hum hum...... Pour ce qui est du nommage, des variables, je répondrais que je m'y retrouve sans pb pour l'instant. En dernier recours, je ferais confiance à mon compilo pour m'insulter si j'avais dû confondre.... :-)

  11. #11
    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
    Par défaut
    Citation Envoyé par simong Voir le message
    en testant ce que je considère être la référence de la variable "&myResults" dans le quick watch, j'ai strictement la même adresse avant d'avoir fait le new, qu'après
    Euh... what ? De quoi tu parles au juste ?
    En .NET on n'a pas accès aux adresses des objets... je sais pas où tu as vu cette adresse, mais ça ne correspond peut-être pas à ce que tu imagines
    En l'occurrence, Result est un type valeur, donc oui, si tu en recrées un nouveau ça va réutiliser le même emplacement sur la pile. Mais ça n'a pas d'importance, vu que l'objet lui-même est toujours copié (et non une référence vers cet objet)

    Citation Envoyé par simong Voir le message
    Hum hum...... Pour ce qui est du nommage, des variables, je répondrais que je m'y retrouve sans pb pour l'instant. En dernier recours, je ferais confiance à mon compilo pour m'insulter si j'avais dû confondre.... :-)
    Bah oui mais faut aussi penser aux gens qui vont relire ton code... si tu travailles tout seul, ça peut passer, mais en équipe la lisibilité est super importante.

  12. #12
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    31
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 31
    Par défaut
    OK, concernant mon test avec le "&" j'ai essayé, détectant dans mon code un phénomène qui me faisait penser à un problème de référence, de me rapprocher de ce que j'aurais fait en C++ (i.e. tester ma référence). Pardon, concernant le "new" que je faisais, c'était sur "myResults", donc un new List<Result>(). Je fais mon "&" dans le QuickWatch de Visual Studio (shift + F9) et ce truc me rendait une info qui ressemblait à une adresse en hexadecimal, que je pensais un temps pouvoir exploiter.... Merci en tout cas de toutes tes précisions !

  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
    Par défaut
    Citation Envoyé par simong Voir le message
    Je fais mon "&" dans le QuickWatch de Visual Studio (shift + F9) et ce truc me rendait une info qui ressemblait à une adresse en hexadecimal, que je pensais un temps pouvoir exploiter....
    Bizarre, moi si je mets ça dans le watch ça me fait une erreur, qui me semble d'ailleurs assez normale :
    Impossible de prendre l'adresse, d'obtenir la taille ou de déclarer un pointeur vers un type managé

  14. #14
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    31
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 31
    Par défaut
    Bonjour, bon bein j'ai résolu mon probème en n'hésitant pas à faire un "new" sur ma variable temporaire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    myResult__ = new Result(n);
    à chaque fois que je passe dans les boucles for où je l'utilise.
    Je ne sais pas si c'est très propre de faire ça, mais ça marche en tout cas. Merci de m'avoir mis sur la piste !

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

Discussions similaires

  1. Réponses: 1
    Dernier message: 14/09/2008, 00h32
  2. A quoi sert une structure (class Struct)
    Par penguin50 dans le forum Ruby
    Réponses: 4
    Dernier message: 06/08/2008, 22h56
  3. Réponses: 1
    Dernier message: 14/07/2008, 11h17
  4. class::struct
    Par ibtisss dans le forum Modules
    Réponses: 3
    Dernier message: 28/01/2006, 23h36

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