Prendre la méthode System.Windows.Forms.Control.Invoke(Delegate method)
Pourquoi est-ce que cela ne donne une erreur de compilation :
Pourtant, cela fonctionne très bien :
Lorsque la méthode attend un délégué ordinaire ?
Prendre la méthode System.Windows.Forms.Control.Invoke(Delegate method)
Pourquoi est-ce que cela ne donne une erreur de compilation :
Pourtant, cela fonctionne très bien :
Lorsque la méthode attend un délégué ordinaire ?
Une expression lambda peut être converti à un type délégué ou une arborescence d'expression - mais il doit savoir ce qui type de délégué. Tout en sachant que la signature n'est pas suffisant. Par exemple, supposons que j'ai:
public delegate void Action1();
public delegate void Action2();
...
Delegate x = () => Console.WriteLine("hi");
Qu'attendez-vous le type concret de l'objet visé par l' x
? Oui, le compilateur peut générer un nouveau type de délégué avec une signature appropriée, mais c'est rarement utile et vous vous retrouvez avec moins de possibilités de contrôle d'erreur.
Si vous voulez le rendre facile pour appeler Control.Invoke
avec un Action
, la meilleure chose à faire est d'ajouter une extension de la méthode de Contrôle:
public static void Invoke(this Control control, Action action)
{
control.Invoke((Delegate) action);
}
Les neuf dixièmes du temps, les gens obtiennent ce parce qu'ils essaient de maréchal sur le thread de l'INTERFACE utilisateur. Voici la manière paresseuse:
static void UI(Action action)
{
System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(action);
}
Maintenant qu'il est tapé, le problème disparaît (qv Skeet de réponse) et nous avons cette très succincte de la syntaxe:
int foo = 5;
public void SomeMethod()
{
var bar = "a string";
UI(() =>
{
//lifting is marvellous, anything in scope where the lambda
//expression is defined is available to the asynch code
someTextBlock.Text = string.Format("{0} = {1}", foo, bar);
});
}
Pour les points de bonus, voici une autre astuce. Vous ne feriez pas cela pour des éléments d'INTERFACE utilisateur, mais dans le cas où vous avez besoin SomeMethod à bloc jusqu'à ce qu'elle se termine (par exemple de demande/réponse d'e/S, l'attente de la réponse) l'utilisation d'un WaitHandle (qv msdn WaitAll, WaitAny, WaitOne).
Notez que AutoResetEvent est un WaitHandle dérivés.
public void BlockingMethod()
{
AutoResetEvent are = new AutoResetEvent(false);
ThreadPool.QueueUserWorkItem ((state) =>
{
//do asynch stuff
are.Set();
});
are.WaitOne(); //don't exit till asynch stuff finishes
}
Et un dernier conseil, parce que les choses peuvent s'emmêler: WaitHandles de décrochage le fil. C'est ce qu'ils sont censés faire. Si vous essayez de maréchal sur le thread d'INTERFACE utilisateur que vous êtes bloqué, votre application se bloque. Dans ce cas, (a) certains graves refactoring est dans l'ordre, et (b) à titre temporaire hack vous pouvez vous attendre:
bool wait = true;
ThreadPool.QueueUserWorkItem ((state) =>
{
//do asynch stuff
wait = false;
});
while (wait) Thread.Sleep(100);
Peter Wone. vous êtes da l'homme. En prenant votre concept un peu plus loin, je suis venu avec ces deux fonctions.
private void UIA(Action action) {this.Invoke(action);}
private T UIF<T>(Func<T> func) {return (T)this.Invoke(func);}
Je place ces deux fonctions dans mon Formulaire d'application, et je peux faire des appels de fond travailleurs comme ceci
int row = 5;
string ip = UIF<string>(() => this.GetIp(row));
bool r = GoPingIt(ip);
UIA(() => this.SetPing(i, r));
Peut-être un peu paresseux, mais je n'ai pas de programme d'installation de travailleur fait des fonctions, ce qui est très pratique dans ce type de cas
private void Ping_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
int count = this.dg.Rows.Count;
System.Threading.Tasks.Parallel.For(0, count, i =>
{
string ip = UIF<string>(() => this.GetIp(i));
bool r = GoPingIt(ip);
UIA(() => this.SetPing(i, r));
});
UIA(() => SetAllControlsEnabled(true));
}
Essentiellement, obtenir des adresses ip à partir d'une interface graphique DataGridView, ping eux, la définition de la résultante des icônes vers le vert ou le rouge, et réactiver les boutons sur le formulaire. Oui, c'est un "parallèle."dans un backgroundworker. Oui il y a BEAUCOUP d'invoquer les frais généraux, mais ses négligeable pour de courtes listes, et beaucoup plus compact code.
J'ai essayé de construire ce sur @Andrey Naumov's réponse. Peut-être que c'est une légère amélioration.
public sealed class Lambda<S>
{
public static Func<S, T> CreateFunc<T>(Func<S, T> func)
{
return func;
}
public static Expression<Func<S, T>> CreateExpression<T>(Expression<Func<S, T>> expression)
{
return expression;
}
public Func<S, T> Func<T>(Func<S, T> func)
{
return func;
}
public Expression<Func<S, T>> Expression<T>(Expression<Func<S, T>> expression)
{
return expression;
}
}
Où le paramètre de type S
est le paramètre formel (le paramètre d'entrée, qui est le minimum requis pour en déduire reste des types). Maintenant, vous pouvez l'appeler comme:
var l = new Lambda<int>();
var d1 = l.Func(x => x.ToString());
var e1 = l.Expression(x => "Hello!");
var d2 = l.Func(x => x + x);
//or if you have only one lambda, consider a static overload
var e2 = Lambda<int>.CreateExpression(x => "Hello!");
Vous pouvez avoir d'autres surcharges pour Action<S>
et Expression<Action<S>>
de même dans la même classe. Pour d'autres , construit en délégué et types d'expression, vous aurez à rédiger des classes séparées, comme Lambda
, Lambda<S, T>
, Lambda<S, T, U>
etc.
L'avantage de ce que je vois sur l'approche originale:
Un de moins spécification du type (uniquement le paramètre formel doit être spécifié).
Qui vous donne la liberté de l'utiliser contre tout Func<int, T>
, et pas seulement lors de l' T
est de le dire, string
, comme indiqué dans les exemples.
Prend en charge les expressions tout de suite. Dans la première approche, vous devez spécifier des types de nouveau, comme:
var e = Lambda<Expression<Func<int, string>>>.Cast(x => "Hello!");
//or in case 'Cast' is an instance member on non-generic 'Lambda' class:
var e = lambda.Cast<Expression<Func<int, string>>>(x => "Hello!");
pour les expressions.
L'extension de la classe pour les autres déléguer (et d'expression) de types est de même lourd comme ci-dessus.
var e = Lambda<Action<int>>.Cast(x => x.ToString());
//or for Expression<Action<T>> if 'Cast' is an instance member on non-generic 'Lambda' class:
var e = lambda.Cast<Expression<Action<int>>>(x => x.ToString());
Dans mon approche, vous devez déclarer les types qu'une seule fois (un de moins pour Func
s).
Une autre façon de mettre en œuvre Andrey réponse est comme ne pas être entièrement générique
public sealed class Lambda<T>
{
public static Func<Func<T, object>, Func<T, object>> Func = x => x;
public static Func<Expression<Func<T, object>>, Expression<Func<T, object>>> Expression = x => x;
}
Donc, les choses réduire à:
var l = Lambda<int>.Expression;
var e1 = l(x => x.ToString());
var e2 = l(x => "Hello!");
var e3 = l(x => x + x);
C'est même moins de frappe, mais vous perdez un certain type de sécurité, et de l'omi, ce n'est pas la peine.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.