web-dev-qa-db-fra.com

Comment utiliser l'action .NET pour exécuter une méthode avec un nombre inconnu de paramètres?

Je souhaite exécuter certaines opérations sur un thread de travail tout en affichant une barre de progression à l'utilisateur. J'ai créé une classe

public class ProgressBar
{
   public void StartAsyncTask(Action action)
   {
      Task t = new Task(action);
      t.start();
   }
}

J'ai découvert que je peux envoyer n'importe quelle méthode au StartAsyncTask de la manière suivante:

  ProgressBar pb = new ProgressBar();
  pb.StartAsyncTask( () => DoSomething(15, "something"));

  public void DoSomething(int i, string s)
   {
      //do something
   }

Tout d'abord, je n'arrive pas à comprendre ce qu'est et comment l'expression lambda - () => - est traduite et comment l'objet Action a passé un délégué avec un nombre inconnu de paramètres.

Je voudrais utiliser un BackgroundWorker avec ma ProgressBar mais dans ce cas, je devrais invoquer l'action. Donc quelque chose comme ça:

void m_backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    Action action = e.Argument as Action; //same action here passed through EventArgs
   //but already in a worker thread so no need for the Task object 

   //and now i need to somehow invoke the action object but i don't know what the parameters are. 

   action.Invoke( ? ); 
}

Comment est-il possible dans le premier exemple d'exécuter l'action sans connaître les paramètres de la méthode StartAsyncTask(Action action)?

Pourquoi dois-je connaître les paramètres lors de l'appel de l'action dans ce cas?

Tout sur comment/pourquoi/quand utiliser "Action" n'est pas très clair pour moi, même si je lis la documentation MSDN et quelques autres threads ici. Toute information à ce sujet m'aidera.

22
Dan Dinu

Je pense que vous pensez un peu trop aux choses. Commençons donc par le haut:

  1. Une expression lambda est une notation pour référencer une exécution de méthode. Exemple:

     x => x + 3
    

    Au niveau le plus élémentaire, cela représente une fonction qui prend 1 entrée, x, puis renvoie une valeur égale à x + 3. Donc dans votre situation, votre expression:

    () => DoSomething(15, "Something")
    

    Représente une méthode prenant 0 paramètres, puis appelant la méthode DoSomething(15, "Something"). Le compilateur est en arrière-plan et traduit cela en un délégué Func ou Action pour vous. C'est donc en effet:

    new Action(delegate()
    {
        DoSomething(15, "Something")
    }); 
    

    La réécriture du compilateur de mon expression simple ci-dessus serait:

    new Func<int, int>(delegate(int x)
    {
        return x + 3;
    });
    
  2. Ensuite, si vous souhaitez invoquer une action plus tard, la syntaxe pour le faire est assez simple:

    Action someMethod = new Action(() => { Console.WriteLine("hello world"); }));
    someMethod(); // Invokes the delegate
    

    Donc, si vous avez une instance Action donnée, il suffit de l'invoquer avec la syntaxe (), Car Action est un délégué qui accepte 0 paramètre et ne renvoie rien.

    Une fonction est tout aussi simple:

    Func<int, int> previousGuy = x => x + 3;
    var result = previousGuy(3); // result is 6
    
  3. Enfin, si vous souhaitez transmettre une méthode à invoquer et que vous n'avez pas de contexte pour les paramètres à ce stade, vous pouvez simplement encapsuler votre appel dans une action et l'invoquer plus tard. Par exemple:

    var myAction = new Action(() =>
         {
              // Some Complex Logic
              DoSomething(15, "Something");
              // More Complex Logic, etc
         });
    
    InvokeLater(myAction);
    
    public void InvokeLater(Action action)
    {
          action();
    }
    

    Toutes les données sont capturées dans une fermeture de votre méthode et sont donc enregistrées. Donc, si vous parvenez à transmettre un Action à votre événement avec la propriété e.Argument, Tout ce que vous aurez à faire serait d'appeler (e.Argument as Action)().

50
Tejs

Ne pouvez-vous pas utiliser DynamicInvoke() sur ce délégué (il faut params object[] args comme argument)

action.DynamicInvoke(arg1, arg2, arg3 );
2
Hulvej