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 :

Assignation d'une methode a une autre


Sujet :

C#

  1. #1
    Acropole
    Invité(e)
    Par défaut Assignation d'une methode a une autre
    Bonjour,
    J'aimerais assigner une méthode a une autre créée dynamiquement comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    DynamicMethod method = new DynamicMethod(style.EventType.ToString(), typeof(void), new Type[] { typeof(void) });
    method = style.OnApply;
    Mais ça me retourne l'erreur suivante :

    Erreur 5 Impossible de convertir le groupe de méthodes 'OnApply' en type non-délégué 'System.Reflection.Emit.DynamicMethod'. Souhaitiez-vous appeler la méthode*?
    Pour info OnApply est de ce type :

    public virtual DynamicMethod OnApply()
    L'idée est de créer une méthode avec un nom (par exemple "OnMouseEnter") et d'assigner OnApply a cette méthode. Quand "OnMouseEnter" serra appellé c'est en fait "OnApply" qui serra exécuté.

  2. #2
    Membre très actif Avatar de polkduran
    Profil pro
    Consultant informatique
    Inscrit en
    Décembre 2009
    Messages
    155
    Détails du profil
    Informations personnelles :
    Âge : 41
    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
    Par défaut
    tu peux voir du côté des delegates

    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    //delegate avec la même signature que style.OnApply
    public delegate void monDelegate();
     
    public void UneAutreMethode(){
      monDelegate del = style.OnApply;
      del(); //on appelle le delegate qui en fait est OnApply
    }


    ou Actions (Action c'est un fait un delegate générique)


    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Action action = style.OnApply;
    action();//appel de l'action

  3. #3
    Acropole
    Invité(e)
    Par défaut
    Sauf que le delegate n'aura pas le nom escompté. Celui ci doit être exactement le nom récupéré dans l'Enum.
    A moins qu'il soit possible de faire ceci, ce dont je doute fortement :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    delegate Enum_X.ToString = object.Apply;
    Le truc c'est que ma méthode marchait mais j'ai changé le dernier paramètre et ça marche plus dutout. Je me souviens pas de ce que j'avais mis.

  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 : 44
    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
    Je ne comprends pas trop ce que tu cherches à faire, ça n'a pas vraiment de sens... pourrais tu détailler le but final ?

  5. #5
    Acropole
    Invité(e)
    Par défaut
    Dans Unity il y'a des évènements pour les objets tels que "OnMouseEnter", "OnMouseExit" etc... J'ai créé une classe "Style" qui ressemble un peu aux CSS en programmation web.
    Lorsque l'évènement survient le style est appliqué en appelant la fonction du style.
    Il n'est donc pas nécessaire de créer les fonctions d'évènements dans la classe de base mais il faut la créer si il y'a un style correspondant.
    De plus ces fonction ne sont pas vraiment des fonctions, j'ai découvert tout a l'heure qu'elles sont invoquées par leur nom, elles n'existent pas dans la classe de base.
    Mais j'ai trouvé un autre moyen. Lorsqu'une de ces fonctions est invoquée elle reste dans une pile que je peux lire à la recherche de fonctions en attente. Je n'ai donc plus qu'a comparer les noms des styles aux nom des fonctions en attente pour appeler la fonction du style si besoin. 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
     
    public void Update()
    {
    	if (IsInvoking())
    	{
    		for (int i = 0; i < Styles.Length; i++)
    		{
    			GUIStyle Style = Styles[i];
    			GUIStyleEvent StyleEvent = Style.EventType;
    			if (IsInvoking(StyleEvent.ToString()) || IsInvoking(Style.EventName))
    			{
    				Style.Apply();
    			}
    		}
    	}
    }
    Donc le problème est résolu mais pas la question de départ. Je tag résolu ?

  6. #6
    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 : 44
    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
    Bah si OnApply renvoie une DynamicMethod, il faut faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    method = style.OnApply();
    (avec les parenthèses, sinon ça fait référence à la méthode sans l'appeler, ce qui n'est valable que pour l'affectation à une variable de type Delegate)
    Par contre, j'ai pas encore très bien compris ce que tu voulais faire, mais j'ai pas l'impression que les DynamicMethod soient la solution... Tu génères du code IL dynamiquement ? Parce que sinon ça sert à rien d'utiliser DynamicMethod...

  7. #7
    Acropole
    Invité(e)
    Par défaut
    Voici mon code dans la classe qui doit recevoir la nouvelle méthode :

    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
     
    // exécuté quand l'objet est créé
    public virtual void Awake()
    {
    for (int i = 0; i < Styles.Length; i++ )
    {
    	GUIStyle style = m_Styles[i];
     
    	if (style)
    	{
    		style = (GUIStyle)ScriptableObject.Instantiate(style);
     
    		if (style.EventType != GUIStyleEvent.None)
    		{
    			Type[] parameters = new Type[] { typeof(void) };
    			DynamicMethod method = new DynamicMethod(style.EventType.ToString(), typeof(void), parameters, this);
    			method = style.Apply();
    		}
    	}
    }
    }
    et le apply dans la classe style (Owner étant la classe ci dessus) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    public virtual void Apply()
    {
    	if (Owner && Owner.animation)
    	{
    		if (!Owner.animation.Play(EventType.ToString()))
    		{
    			Debug.Log("can't play animation");
    		}
    	} else Debug.Log("no owner or no animation");
     
    	Type[] parameters = new Type[] { typeof(void) };
    	return new DynamicMethod(style.EventType.ToString(), typeof(void), parameters, Owner);
    }
    Ce qui devrait exécuter "Apply" quand la méthode dynamique est appelé, par exemple si style.EventType = "OnMouseOver".

    Tout est exécuté au départ puis plus rien.

  8. #8
    Membre très actif Avatar de polkduran
    Profil pro
    Consultant informatique
    Inscrit en
    Décembre 2009
    Messages
    155
    Détails du profil
    Informations personnelles :
    Âge : 41
    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
    Par défaut
    C'est peut-être que ta méthode Apply est de type void et tu lui fait renvoyer qq chose. Et si tu essayes de spécifier dans la signature de Apply que tu renvoies une DynamicMethod

  9. #9
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    Grosso modo, tu souhaites créer un clone de Style.Apply mais avec un nom différent ?

    Ça semble en effet assez tordu mais je ne connais pas Unity. Le plus simple est sans doute d'utiliser DynamicMethod pour émettre un appel vers Style.Apply.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    public void CreateMethod(Object instance)
    {
       DynamicMethod dm = new DynamicMethod(style.EventType.ToString(), typeof(void), new Type[] { typeof(void) }, instance.GetType());
       var g = dm.GetILGenerator();
       g.Emit(OpCodes.Ldarg_0);  // Charge this
       g.Emit(OpCodes.Callvirt, instance.GetType().GetMethod("Apply"));
       g.Emit(OpCodes.Ret);
       dm.CreateDelegate(typeof(Action), instance);
    }

  10. #10
    Acropole
    Invité(e)
    Par défaut
    Citation Envoyé par polkduran Voir le message
    C'est peut-être que ta méthode Apply est de type void et tu lui fait renvoyer qq chose. Et si tu essayes de spécifier dans la signature de Apply que tu renvoies une DynamicMethod
    J'aimerais bien, malheureusement c'est un mauvais copier/collé. J'ai du réécrire la méthode pour la poster. J'ai été un peu vite en besogne.

    J'ai pas le temps de poster plus de détails. J'en reparle ce soir.

  11. #11
    Acropole
    Invité(e)
    Par défaut
    Re.

    Je vais essayer d'exposer le problème dans les moindres détails.

    Les objets, dans unity, héritent de la classe MonoBehaviour, pour la plus part. C'est cette classe que j'utilise comme base car elle possède les méthodes qui m'interressent : OnMouseEnter, OnMouseOver, OnMouseExit, OnMouseDown... etc.
    Sauf que ces fonctions n'existent pas en réalité !
    Comme on peut le voir dans le code de la classe (généré par reflector) :

    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
    public class MonoBehaviour : Behaviour
    {
        // Methods
        [MethodImpl(MethodImplOptions.InternalCall), WrapperlessIcall]
        public extern MonoBehaviour();
        public void CancelInvoke();
        [MethodImpl(MethodImplOptions.InternalCall), WrapperlessIcall]
        public extern void CancelInvoke(string methodName);
        [MethodImpl(MethodImplOptions.InternalCall), WrapperlessIcall]
        private extern void Internal_CancelInvokeAll();
        [MethodImpl(MethodImplOptions.InternalCall), WrapperlessIcall]
        private extern bool Internal_IsInvokingAll();
        [MethodImpl(MethodImplOptions.InternalCall), WrapperlessIcall]
        public extern void Invoke(string methodName, float time);
        [MethodImpl(MethodImplOptions.InternalCall), WrapperlessIcall]
        public extern void InvokeRepeating(string methodName, float time, float repeatRate);
        public bool IsInvoking();
        [MethodImpl(MethodImplOptions.InternalCall), WrapperlessIcall]
        public extern bool IsInvoking(string methodName);
        public static void print(object message);
        public Coroutine StartCoroutine(IEnumerator routine);
        public Coroutine StartCoroutine(string methodName);
        [MethodImpl(MethodImplOptions.InternalCall), WrapperlessIcall]
        public extern Coroutine StartCoroutine(string methodName, object value);
        [MethodImpl(MethodImplOptions.InternalCall), WrapperlessIcall]
        public extern Coroutine StartCoroutine_Auto(IEnumerator routine);
        [MethodImpl(MethodImplOptions.InternalCall), WrapperlessIcall]
        public extern void StopAllCoroutines();
        [MethodImpl(MethodImplOptions.InternalCall), WrapperlessIcall]
        public extern void StopCoroutine(string methodName);
     
        // Properties
        public bool useGUILayout { [MethodImpl(MethodImplOptions.InternalCall), WrapperlessIcall] get; [MethodImpl(MethodImplOptions.InternalCall), WrapperlessIcall] set; }
    }
    En fait ces fonctions sont appelées dans la classe SenMouseEvents :

    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
     
    MouseOverPair lhs = pairArray[i];
            if (MouseOverPair.ComparePair(lhs, m_OldMouseOver[i]))
            {
                if (lhs != 0)
                {
                    lhs.SendMessage("OnMouseOver");
                }
            }
            else
            {
                if (m_OldMouseOver[i] != 0)
                {
                    m_OldMouseOver[i].SendMessage("OnMouseExit");
                }
                if (lhs != 0)
                {
                    lhs.SendMessage("OnMouseEnter");
                    lhs.SendMessage("OnMouseOver");
                }
            }
            m_OldMouseOver[i] = lhs;
    La solution classique est d'implémenter ces fonctions dans ma classe, sauf qu'ici le comportement ne dépend pas de la classe mais de l'objet.
    Le comportement du click par exemple ne serra quasiment jamais le même d'un objet a un autre.
    J'ai donc choisi de créer une classe a part pour gérer ça et que j'ajoute a la liste des membres de l'objet initial.
    Evidemment je pourrai implémenter toutes ces méthodes dans la classe parent mais ça veut dire qu'elles seront toutes appelées tout le temps même s'il n'y a rien a faire 90% du temps.
    C'est le plan Z.
    Le plan A était de générer dynamiquement ces méthodes pour qu'il n'y ai que celles nécessaires, ni plus, ni moins.

    Petit problème, il est impossible de savoir si les méthode en question sont traitées à la compilation ou a l’exécution. Dans le premier cas il se pourrait qu'une liste des méthodes disponibles soit créée à la compilation et dans ce cas aucune méthode rajoutée a l'exécution ne serra jamais appelé...

    J'espère avoir été plus clair.
    Merci pour votre patience.

  12. #12
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    Merci pour les explications. Donc, si j'ai bien compris, tu souhaiterais que deux objets d'une même classe puissent s'autoajouter à la volée des méthodes distinctes ?

    Ce n'est pas possible. D'une part les méthodes appartiennent à la classe et non à l'objet (typage fort, rien à voir avec du javascript). D'autre part même une classe ne peut pas se voir ajouter des membres à la volée.

    Tu pourrais à la rigueur faire cela avec des types dynamiques (voir ExpandoObject sur msdn). Mais d'une part il y a 99% de chances que l'implémentation de SendMessage dans Unity ne colle pas avec cette astuce et, d'autre part, il y a 99% de chances que ce soit encore pire au niveau des perfs que de déclarer toutes les méthodes sur tous les objets.

    Cela dit, tout ça fleure bon l'optimisation prématurée. Et mauvaise qui plus est. D'abord parce que le moteur de Unity utilise la reflexion pour obtenir la méthode à invoquer puis invoque cette méthode. Clairement, ici, c'est l'utilisation de la réflexion qui va être coûteuse, pas l'invocation d'une méthode capable de détecter rapidement qu'il n'y a aucun traitement à effectuer et de quitter. Avant de t'engager dans des solutions saugrenues, je t'en prie, fais un test rapide de l'impact sur les performances (entre une classe n'implémentant aucun événement et une autre implémentant un handler de clic), je parie que tu ne verras pas la différence. Ensuite, s'il s'agit simplement de réagir à des clics souris (le genre d'événement qui se produit une fois par seconde), on se fiche totalement d'optimiser ça.


    A mes yeux, ton seul problème est de savoir comment réagir différemment à un événement selon le style associé à l'objet. Tu peux te tourner vers la génération dynamique de code si tu es vraiment certain que c'est nécessaire. Une bonne approche serait alors celle-ci, qui permet aux objets de définir leur comportement à la volée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    {
       Action m_onMouseEnter;
     
       public void Apply(Style style)
       {
           this.m_onMouseEnter = style.CreateOnMouseEnter();
       }
     
       public void OnMouseEnter()
       {
          this.m_onMouseEnterDelegate();
       }
    Tu pourrais aussi revoir ta hiérarchie de classe, tes objets ayant cette fois des classes différentes. Il est aussi possible de générer des classes dynamiquement, peuplant leurs listes de membres comme tu le voudras avant de les sceller et de les instancier. SandCastle ou d'autres biblios pourraient peut-être t'aider à faire ça.

  13. #13
    Acropole
    Invité(e)
    Par défaut
    Citation Envoyé par DonQuiche Voir le message
    Donc, si j'ai bien compris, tu souhaiterais que deux objets d'une même classe puissent s'autoajouter à la volée des méthodes distinctes ?
    C'est exactement ça
    Effectivement je me prend peut être la tête pour rien.
    Merci !

  14. #14
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    Je t'en prie.

    Note que j'ai un peu retravaillé le message, n'hésite pas à rejeter un coup d'oeil, tu pourrais y trouver deux ou trois trucs intéressants mais puisque tu sembles d'accord avec moi pour dire que tu prends sans doute trop la tête...

  15. #15
    Membre très actif Avatar de polkduran
    Profil pro
    Consultant informatique
    Inscrit en
    Décembre 2009
    Messages
    155
    Détails du profil
    Informations personnelles :
    Âge : 41
    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
    Par défaut
    ton problème me fait penser un peu au design pattern "Mediator", c'est l'intermédiaire entre des objets qui envoient des "messages" sans savoir qui va le recevoir; et d'autres objets qui "s'abonnent" aux "messages" qui peut être dans ton cas dynamiquement. Un objet peut également se désabonner d'un message.

    Par exemple

    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    //envoi d'un message sans savoir qui va le traiter
    Mediator.SendMessage("nomDuMessage",parametres);
     
    //abonnement à un message en lui passant un delegate ou une méthode qui traitera le message
    Mediator.Subscribe("nomDuMessage",unDelegate);


    http://www.dofactory.com/Patterns/PatternMediator.aspx

    http://sourcemaking.com/design_patterns/mediator/c%2523

Discussions similaires

  1. Réponses: 14
    Dernier message: 10/09/2009, 19h08
  2. [XL-2007] Afficher une checkbox dans une feuille si une checkbox d'une autre feuille est cochée
    Par JessieCoutas dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 18/08/2009, 13h35
  3. Réponses: 7
    Dernier message: 17/01/2009, 13h10
  4. Recherche une valeur d'une cellule dans une colonne d'une autre feuille
    Par kourria dans le forum Macros et VBA Excel
    Réponses: 8
    Dernier message: 21/06/2007, 13h48
  5. Appliquer une methode a une variable d une classe ?
    Par Slumpy dans le forum VB.NET
    Réponses: 18
    Dernier message: 07/06/2007, 17h17

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