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

Windows Forms Discussion :

[C#]Complément d'infos sur évènements


Sujet :

Windows Forms

  1. #1
    Membre éprouvé
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Points : 1 050
    Points
    1 050
    Par défaut [C#]Complément d'infos sur évènements
    Bonjour,

    Jusqu'à présent, j'ai utilisé les événements sans rencontrer de problèmes particuliers.

    Par exemple, si dans une classe dérivant de "Control" j'ai besoin d'un nouvel événement, je déclare simplement l'événement et je crée une méthode d'appel (pour un EventHandler de base et un EventArgs classique, sinon évidemment je dérive EventArgs et je crée mon propre délégué) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
            public event EventHandler ValuePropChanged;
     
            protected virtual void OnValuePropChanged(EventArgs e)
            {
                if (ValuePropChanged != null)
                    ValuePropChanged(this, e);
            }
    Ca semble ne poser aucun problème, c'est comme ça que j'avais compris la chose dès le début.

    Or, voici qu'en cherchant autre chose, je tombe sur ceci dans un contrôle du framework, dérivé aussi de "Control" :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public event EventHandler ValueChanged
    {
        add
        {
            base.Events.AddHandler(EVENT_VALUECHANGED, value);
        }
        remove
        {
            base.Events.RemoveHandler(EVENT_VALUECHANGED, value);
        }
    }
    Donc, ajout explicite des méthodes Add et Remove et appel de AddHandler et RemoveHandler de la classe de base.

    Du coup, j'ai l'impression d'avoir raté quelque chose.
    Quelle est la différence de fonctionnement entre ma méthode et la méthode "officielle", et surtout si ces appels sont nécessaires, pourquoi mon code fonctionne-t-il?

    J'ai désassemblé mon propre code pour voir si le compilateur n'avait pas ajouté du code implicite, mais non, rien du tout.

    Merci d'avance,

    Claude

  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
    Si je ne me trompe, c'est pour des raisons de perf.

    Comme t'as pu le voir, les contrôles winforms ont une tripotée d'événements, de l'ordre de plusieurs dizaines. Quand tu déclares un event simple, sans surcharger add et remove, il me semble que le compilo C# crée une variable membre, qui contiendra les références des méthodes abonnées. Donc, par défaut, on aurait autant de ces membres que l'on a d'event. Ce qui est du gâchis de mémoire, puisqu'il est rare que l'on s'abonne a plus de trois ou quatre événements.
    Donc là, visiblement, le code que tu as vu par réflexion met toutes les références à toutes les méthodes abonnées à tous les event dans une unique collection.

    C'est aussi pour ça, qu'en MFC/Win32, les composants d'UI ne sont pas étendus avec des méthodes virtuelles à surcharger, mais par l'ajout de méthodes à une Message Map.

    Donc : à moins que tu ne crées un composant avec des dizaines d'events auxquels tu ne t'abonnes pas, pas la peine de se prendre la tête à surcharger add et remove
    ಠ_ಠ

  3. #3
    Membre éprouvé
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Points : 1 050
    Points
    1 050
    Par défaut
    Merci

    Donc, si je suis bien le raisonnement, lorsque j'écris ceci

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    [EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
    public event MouseEventHandler MouseClick
    sur une série d'événements pour cacher ceux qui ne m'intéressent pas, en fait je détruits cette optimisation puisque ça va recréer la variable d'instance. Je devrais alors écrire ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
    public event MouseEventHandler MouseClick
    {
        add
        {
            base.MouseClick += value;
        }
        remove
        {
            base.MouseClick -= value;
        }
    }
    voir ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
    public event MouseEventHandler MouseClick
    {
        add
        {
             return;
        }
        remove
        {
             return;
        }
    }

    ce qui éviterait sa création?

    Par contre, quelque chose me chiffonne dans ce concept (je suis plus habitué des petits systèmes en très bas niveau) :

    La mémoire programme et la mémoire donnée sur un PC, c'est la même physiquement. Et donc, ça m'interpelle que pour tenter, pour un évènement donné, de gagner l'emplacement d'une seule variable en ajoutant une surcharge à add et Remove (donc du code, donc de la mémoire) produit au final un gain mémoire. Je n'ai rien trouvé de static dans ces procédures et donc on a le code pour chaque instance du contrôle créée, non?
    Quelque chose doit m'échapper, LOL.

    Claude

  4. #4
    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
    Ah oui, j'avais oublié que c'était toi qui voulais aussi modifier la façon dont les propriétés sont initialisées dans le Designer.cs Tu cherches à masquer des event dans le designer ?
    Un peu de google m'amène là : http://discuss.fogcreek.com/dotnetqu...ow&ixPost=1138
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [Browsable(false),EditorBrowsable(EditorBrowsableState.Never)]
    public event EventHandler DoubleClick
    {
        add
        {
            base.DoubleClick+=value;
        }
        remove
        {
            base.DoubleClick-=value;
        }
    }
    La même solution que celle que tu suggères, donc.

    Pour ce qui est du gain mémoire : le code, il est qu'une seule fois en mémoire, contrairement aux membres, qui eux sont dans chaque instance. Ce qu'on a voulu minimiser, c'est la place occupée par chaque instance.
    ಠ_ಠ

  5. #5
    Membre éprouvé
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Points : 1 050
    Points
    1 050
    Par défaut
    Ah oui, j'avais oublié que c'était toi qui voulais aussi modifier la façon dont les propriétés sont initialisées dans le Designer.cs
    Oui, et j'y suis arrivé, du moins en partie. J'arrive à imposer un constructeur bien particulier par exemple, ou à modifier les paramètres passés au constructeur par le designer J'ai aussi compris qu'en remontant la hiérarchie du TypeConverter on pouvait arriver jusqu'aux constructions élémentaires, mais je ne suis pas allé si loin, imposer un constructeur me suffisait déjà.

    La même solution que celle que tu suggères, donc.
    Ok, alors ça veut dire que j'ai correctement compris ce que tu m'expliques, et ça infirme par contre plein d'exemples trouvés sur le net, comme quoi il faut toujours se méfier.

    le code, il est qu'une seule fois en mémoire, contrairement aux membres, qui eux sont dans chaque instance. Ce qu'on a voulu minimiser, c'est la place occupée par chaque instance.
    C'est là où manifestement je me suis planté dans le raisonnement. Je croyais que le code d'une méthode statique n'était qu'une fois en mémoire même en l'absence d'instance, mais que le code d'une méthode instanciée s'y trouvait autant de fois que d'instances créées et non une seule fois.

    Si je prends ça en compte, alors (si j'ai bien compris) l'optimisation est surtout efficace non pas si on a un grand nombre d'événements inutilisés, mais surtout si on a un grand nombre d'instances du contrôle créées. Dans ce cas, même avec un seul événement le gain peut être significatif, non?

    Merci,
    Claude

  6. #6
    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 ClaudeBg Voir le message
    Si je prends ça en compte, alors (si j'ai bien compris) l'optimisation est surtout efficace non pas si on a un grand nombre d'événements inutilisés, mais surtout si on a un grand nombre d'instances du contrôle créées. Dans ce cas, même avec un seul événement le gain peut être significatif, non?
    Ben c'est le produit des deux, logiquement. Coût total = coût unitaire * quantité.

    Mais dans ton cas, qui est celui de tous ceux présents sur ce forum, comme nous ne sommes pas concepteurs de bibliothèques de contrôles généralistes (combo, textbox, ...), on peut oublier ce genre de souci et créer gaiement nos événements avec la syntaxe classique pour nos contrôles spécialisés. Même si c'est toujours rigolo de voir comment ça fonctionne

    Quant au coup ds méthodes d'instance : une méthode d'instance, en programmation objet, n'est jamais qu'une méthode classique avec un paramètre caché, nommé "this", vers le type de l'instance courante. Ce n'est que de la syntaxe.
    ಠ_ಠ

  7. #7
    Membre éprouvé
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Points : 1 050
    Points
    1 050
    Par défaut
    Salut
    -----

    Grand merci pour les explications, et désolé d'approfondir autant

    Ben c'est le produit des deux, logiquement. Coût total = coût unitaire * quantité
    Ce que je voulais dire, c'est que pour une instance d'un objet unique, le gain pourrait être nul, voire négatif, même s'il y a plein d'évènements, parce que pour chacun des événements on gagne une instance de variable mais on perd au niveau de la place du code, et que ce code doit s'y trouver autant de fois que d'événements même si on n'a qu'une seule instance.

    dit autrement, si j'ai bien compris tes explications :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Gain en mémoire pour une seule instance = (nombre d'événements) * (mémoire utilisée pour une instance de variable d'événement - mémoire utilisée pour le code économisant l'instance)
    Reste à déterminer si ce gain est positif ou négatif, je l'ignore.

    Par contre, si on a peu ou un seul évènement, mais beaucoup d'instances de l'objet, le gain serait d'office significatif, car le code n'y est qu'une fois et l'instance de variable un grand nombre de fois.

    dit autrement :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Gain en mémoire pour x instances au niveau d'un seul évènement = (nombre d'instances * taille d'une instance de variable d'événement) - mémoire occupée par un code unique.
    Et là, plus le nombre d'instances est grand, plus le gain est significatif.

    Et évidemment, si on combine beaucoup d'instances avec beaucoup d'événements c'est encore plus significatif (mais donc pas forcément beaucoup d'événements avec peu d'instances).

    Ce qui me fait dire que la question à se poser pour utiliser ou non cette méthode serait plutôt au vu de tes explications : "Mon contrôle risque-t-il d'être instancié un grand nombre de fois ou pas dans un thread donné?"

    Non?

    Sinon :

    Mais dans ton cas, qui est celui de tous ceux présents sur ce forum, comme nous ne sommes pas concepteurs de bibliothèques de contrôles généralistes (combo, textbox, ...),
    Ben si justement, LOL.
    Je suis en train de créer une bibliothèque de contrôles qui ont des spécificités par rapport au genre d'application que je développe (des trackbars qui sont de vrais trackbars, des DateTimePicker avec des fonctionnalités spéciales, des contrôles de saisie numériques et hexadécimaux (pas une bête interception des KeyDown), des IpBoxes efficaces, des contrôles spécifiques d'affichages de grandeurs analogiques, des contrôles de signalisation, etc.

    C'est pour ça que je cherche à bien cerner tous les détails et à faire de la programmation propre. Ca va être la base de mon projet suivant (et de la suite), qui tournait avant en VB6 et que je dois intégralement réécrire.

    Note que tout ça sera disponible en open-source, et donc je dois quand même veiller à ne pas distribuer n'importe quoi. Ca explique mes questions un peu particulières

    Je suis probablement un programmeur un peu "atypique" car mon but est le pilotage de cartes à microcontroleurs personnelles, elles-mêmes programmées en langage d'assemblage.


    Claude

  8. #8
    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
    L'optimisation est un sujet compliqué. Le plus important n'est pas de le faire mais de se demander si ça vaut vraiment la peine.

    Les outils comme .Net sont faits pour fournir une solution satisfaisante selon différents critères (perf, facilité d'utilisation, extensibilité) à 90% des cas.

    Entre autres, optimiser la taille du code est très très bas dans la liste des priorités. Bien en dessous de l'optimisation de la taille des instances, qui est déjà très bas, derrière la doc, la lisibilité, la simplicité, et surtout le fait que ça marche

    Et un bon moyen d'apprendre, c'est par l'exemple : tu trouveras sûrement plusieurs librairies de contrôles sur le net, qui seront avec un peu de chance bien documentés, et dont tu pourrais t'inspirer.
    ಠ_ಠ

  9. #9
    Membre éprouvé
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Points : 1 050
    Points
    1 050
    Par défaut
    Merci de tes précieux conseils

    Je vais donc pour mes contrôles préférer la lisibilité sur l'optimisation. Du reste, j'avais déjà précisé que je me méfiais du terme "optimiser" dans une autre discussion, surtout dans un sens "générique".

    A titre de pure information, j'ai pisté la méthode utilisée, et d'après ce que j'en ai compris (n'hésite pas à me rectifier) :

    1) On remplace une variable locale d'instanciation de type event par une propriété de type event dans laquelle on surcharge add et remove

    2) Cette propriété ne fait en fait plus référence à une variable instanciée de type event, MAIS à une variable statique de type object (là est le gain et toute l'astuce, en fait).

    3) Cette variable statique est initialisée sur base d'un objet vide dans le constructeur statique de la classe

    4) L'objet statique est utilisé par add et remove, qui stocke l'objet dans une liste d'événements unique.

    5) Dans la méthode d'appel, on récupère une instance par casting sur l'objet , et si non nulle on appelle.

    Bref, c'est un mécanisme assez lourd (et qui nécessite boxing/unboxing) à mettre en place pour un nouvel événement (même si une bonne partie du travail est déjà fait dans la classe de base), et le gain n'est probablement sensible que si on a beaucoup d'instances de l'objet.

    Merci

    Claude

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

Discussions similaires

  1. Réponses: 20
    Dernier message: 16/10/2009, 14h29
  2. Complément d'Infos sur les .ini
    Par Fred2209 dans le forum Visual C++
    Réponses: 4
    Dernier message: 27/12/2006, 09h59
  3. recherche complément d'info sur instanceof
    Par SpaceFrog dans le forum Général JavaScript
    Réponses: 16
    Dernier message: 23/11/2006, 09h55
  4. Complément d'info sur l'utilisation de settimeout
    Par WhyMee dans le forum Général JavaScript
    Réponses: 4
    Dernier message: 18/08/2006, 13h08
  5. [CR] Infos sur l'utilisation de dll
    Par step dans le forum SAP Crystal Reports
    Réponses: 11
    Dernier message: 09/08/2002, 11h35

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