Partage
  • Partager sur Facebook
  • Partager sur Twitter

Reflection avec delegates & generics

    10 mai 2018 à 10:36:41

    Bonjours,

    J'essaye de réaliser quelque chose d'un peu tordu sur unity.

    J'ai un système d’évent, qui se connecte comme ceci :

    m_subscriberList.Add(new Event<TestEvent>.Subscriber(onEvent));

    Avec :
    Event un type générique (acceptant un EventArg)
    TestEvent, l'event qui est écouté, Subscriber un type héritant de IEventSubscriber
    onEvent une fonction de type void(TestEvent), récupérée sous la forme d'une Action<TestEvent>
    m_subscriberList une List<IEventSubscriber>.

    Je voudrais pouvoir indiquer un nom d’événement dans l'inspector (ici "TestEvent"), et pouvoir connecter l’évent demandé dynamiquement (en vu de créer un système de tuto).

    Voici ce que j'ai réalisé

    Type t = Type.GetType(eventName);
    if(t == null)
    {
        Debug.LogError("Can't find the event type " + eventName);
        return;
    }
    Type eventType = typeof(Event<>);
    var s = eventType.MakeGenericType(new Type[] { t }).GetNestedType("Subscriber");
    
    var m = typeof(TutorialLogic).GetMethod("onEvent", BindingFlags.NonPublic | BindingFlags.Instance);
    var genericM = m.MakeGenericMethod(new Type[] { t });
    
    var action = typeof(Action<>).MakeGenericType(new Type[] { t });
    
    m_subscriberList.Add((IEventSubscriber)s.GetConstructors()[0].Invoke(new object[] { Delegate.CreateDelegate(action, this, genericM) }));
    

    J'obtiens l'exception suivante au runtime, à la création du subscriber (dernière ligne) :

    ArgumentException: failed to convert parameters
    System.Reflection.MonoCMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:484)
    System.Reflection.MonoCMethod.Invoke (BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:528)
    System.Reflection.ConstructorInfo.Invoke (System.Object[] parameters) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/ConstructorInfo.cs:77)
    TutorialLogic.startNext () (at Assets/Scripts/Tutorial/TutorialLogic.cs:63)
    TutorialLogic.Start () (at Assets/Scripts/Tutorial/TutorialLogic.cs:37)
    


    Merci pour votre aide :)

    • Partager sur Facebook
    • Partager sur Twitter
    "Tout devrait être rendu aussi simple que possible, mais pas plus." A.Einstein
      15 mai 2018 à 15:10:31

      Je trouve curieux de devoir passer par de la reflection pour faire ce genre de tâche. Je pense que tu aurais un système un peu plus robuste et performant si tu poussais l'usage des interfaces un peu plus loin.

      Cela dit...Je ne suis pas certain que l'équivalent de "onEvent" soit "[...].GetMethod("onEvent", [...]).MakeGenericMethod([...])". Si "onEvent" est effectivement une méthode dont la signature est du style "void(TestEvent)", il ne s'agit aucunement d'une méthode générique. Je suis même étonné que le code ne plante pas sur "MakeGenericMethod" étant donné que tu tente de faire une liaison impossible à résoudre (spécialiser une méthode générique alors que la méthode en question n'accepte aucun argument de généricité). Le MakeGenericMethod est de trop. Tente sans ça, ça devrait mieux fonctionner plus bas. Le MakeGenericMethod aurait eu du sens si ta méthode avait été "onEvent<T>" de signature "void<T>(T)" (+ contrainte si nécessaire).

      -
      Edité par Nisnor 15 mai 2018 à 15:11:02

      • Partager sur Facebook
      • Partager sur Twitter
        23 mai 2018 à 11:25:13

        C'est une coquille qui s'est glissé dans mon text, je testais le fonctionnement de mon système avec un event de ce nom, mais sinon oui, onEvent est bien un générique (sinon je ne pourrais pas lui connecter un event d'un autre type).
        Je ne vois pas trop comment les interfaces me permettrais de rendre ceci plus efficace, il me faut dans tous les cas passer par l'inspector, donc récupérer un nom.

        Entre temps je suis passé par une solution plus simple, mais beaucoup moins élégante, en hardcodant les events dont j'ai besoin.

        Bien que je n'en ai plus besoin actuellement, je laisse le sujet ouvert, car la réponse m’intéresse.

        • Partager sur Facebook
        • Partager sur Twitter
        "Tout devrait être rendu aussi simple que possible, mais pas plus." A.Einstein

        Reflection avec delegates & generics

        × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
        × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
        • Editeur
        • Markdown