Un modo migliore per scrivere il metodo di estensione per richiamare un controllo?

Ho questa funzione generale per richiamare un controllo WinForm:

public static void Invoke(this Control c, Action action) { if (c.InvokeRequired) c.TopLevelControl.Invoke(action); else action(); } 

Sto pensando di renderlo migliore, introducendo limiti più severi per evitare che cose prive di significato possano essere come:

 button1.Invoke(() => list.Add(1)); 

Inoltre può esserci una digitazione ridondante come:

 button1.Invoke(() => button1.Hide()); 

poiché stiamo già specificando che this è button1 .

Quindi l’ho fatto:

 public static void Invoke(this T c, Action action) where T : Control { if (c.InvokeRequired) c.TopLevelControl.Invoke(action); else action(c); } 

Ora dovrò chiamare,

 button1.Invoke((c) => c.Hide()); 

o

 button1.Invoke((c) => button1.Hide()); 

Ora penso che anche in quel caso ci sia qualcosa in più del tipo richiesto. Se sto specificando this è button1 , quindi nell’espressione lambda non voglio specificare una variabile dummy c nuovo per dire dove operare. C’è comunque posso rendere questo più breve di nuovo? Forse piace

 button1.Invoke(Hide); 

o

 button1.Hide.Invoke(); 

o così in C #?

Prima lasciatemi dire che potresti essere troppo pensieroso: il codice breve è una cosa grandiosa, ma c’è un punto in cui inizia a essere fonte di confusione per chiunque tenti di leggere il codice.

Ora, il tuo primo suggerimento:

 button1.Invoke(Hide); 

potrebbe funzionare, se lo fai:

 button1.Invoke(button1.Hide); 

perché altrimenti il ​​compilatore non può sapere, dove cercare il metodo Hide (). Potrebbe anche causare alcuni comportamenti strani, per esempio se tutto questo codice fosse in qualche class derivata, come questo:

 class A : Control { public A() { Button button1=new Button(); button1.Invoke(Hide); } } 

Ora si compilerebbe, ma il metodo Hide () sarebbe il metodo Hide () dell’intero controllo, non il pulsante! Il modo per ottenere questo è semplicemente:

 public static void Invoke(this Control c, Action action) { c.Invoke(action); } 

In questo modo:

 button1.Hide().Invoke(); 

funzionerebbe anche senza aggiungere metodi di estensione, devi solo farlo:

 ((Action)button1.Hide).Invoke(); 

Questo ovviamente significa che il metodo Hide () viene richiamato nel thread corrente, che probabilmente non è quello che vuoi. Quindi fallo:

 ((Action)button1.Hide).Invoke(button1); public static void Invoke(this Action action, Control c) { c.Invoke(action); } 

Ci scusiamo per la lunga risposta, spero che aiuti.

Per build altre risposte, inserirò questo in una class di estensione separata.

 public static void Invoke(this T c, Action action) where T : Control { if (c.InvokeRequired) c.Invoke(new Action>(Invoke), new object[] { c, action }); else action(c); } 

Ciò impedirà il TargetParameterCountException di TargetParameterCountException durante il cross-threading.

Chiamare:

 button1.Invoke(x => x.Hide()); 

È ansible utilizzare SynchronizationContext.Post o SynchronizationContext.Send per fare eseguire il marshalling dell’interfaccia al thread dell’interfaccia utente, che sia Windows Form o WPF. Il metodo statico SynchronizationContext.Current restituirà un contesto di sincronizzazione appropriato per il tipo di applicazione.

Post viene eseguito in modo asincrono durante l’invio di blocchi fino al termine dell’azione.

Il seguente codice nasconderà un pulsante in modo asincrono:

 SynchronizationContext.Current.Post(_=>button1.Hide(),null); 

Vorrei andare con:

 public static void Invoke(this T c, Action action) where T : Control { if (c.InvokeRequired) c.TopLevelControl.Invoke(action); else action(c); } 

e

 button.Invoke(c => c.Hide()); 

È quello più pulito (ti viene dato il pulsante originariamente specificato per eseguire l’azione) e più sicuro (non devi specificare il button1 due volte … ti viene restituito come parametro per il tuo lambda ). Credo che questa sia una syntax elegante.

Non può assolutamente essere fatto come button1.Invoke(Hide); o button1.Hide.Invoke(); a causa delle restrizioni di syntax C #.

Ma se sei disposto a rinunciare a IntelliSense, puoi renderlo un po ‘più breve. Come svantaggio, alcuni bug che di solito possono essere rilevati e risolti durante la compilazione (come errori di battitura o parametri di corrispondenza errata) diventeranno errori di runtime. A volte è accettabile, a volte no.

Guardando al futuro, ecco un esempio di utilizzo :

 button1.Invoke("Hide"); 

o

 button1.Invoke("ResumeLayout", true); 

Soluzione:

 internal static class ExtensionMethods { internal static object Invoke(this TControl control, string methodName, params object[] parameters) where TControl : Control { object result; if (control == null) throw new ArgumentNullException("control"); if (string.IsNullOrEmpty(methodName)) throw new ArgumentNullException("methodName"); if (control.InvokeRequired) result = control.Invoke(new MethodInvoker(() => Invoke(control, methodName, parameters))); else { MethodInfo mi = null; if (parameters != null && parameters.Length > 0) { Type[] types = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { if (parameters[i] != null) types[i] = parameters[i].GetType(); } mi = control.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public, null, types, null); } else mi = control.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public); if (mi == null) throw new InvalidOperationException(methodName); result = mi.Invoke(control, parameters); } return result; }