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.
Je pense que vous pensez un peu trop aux choses. Commençons donc par le haut:
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;
});
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
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)()
.
Ne pouvez-vous pas utiliser DynamicInvoke()
sur ce délégué (il faut params object[] args
comme argument)
action.DynamicInvoke(arg1, arg2, arg3 );