¿La mejor forma de invocar un código cruzado?

Sé que esta pregunta ya se hizo antes, pero estoy buscando una forma de:

  1. optimizar la creación de código seguro de subprocesos cruzados.
  2. reutilice este código en cualquier situación (sin referencias a Windows Forms).

Esto es lo que tengo hasta ahora, pero quiero eliminar las referencias de Windows Forms. ¿Algunas ideas?

public delegate void SafeInvokeDelegate(System.Action action); public class SafeInvoke { private readonly System.Windows.Forms.Control _threadControl; public SafeInvoke() { _threadControl = new System.Windows.Forms.Control(); } public void Invoke(System.Action action) { if (_threadControl.InvokeRequired) _threadControl.Invoke(new SafeInvokeDelegate(Invoke), new object[] {action}); else if (action != null) action(); } } 

La clase anterior se puede usar de esta manera:

 SafeInvoke _safeInvoker = new SafeInvoke(); void SafeClearItems() { _safeInvoker.Invoke(delegate { listView1.Items.Clear(); }); } 

¿Cómo eliminaría System.Windows.Forms.Control en la clase SafeInvoke pero mantendría la misma funcionalidad?

También podría usar un método de extensión y lambdas para hacer su código mucho más limpio.

 using System.ComponentModel; public static class ISynchronizeInvokeExtensions { public static void InvokeEx(this T @this, Action action) where T : ISynchronizeInvoke { if (@this.InvokeRequired) { @this.Invoke(action, new object[] { @this }); } else { action(@this); } } } 

Entonces ahora puede usar InvokeEx en cualquier ISynchronizeInvoke y poder acceder a las propiedades y campos de implementación de clase.

 this.InvokeEx(f => f.listView1.Items.Clear()); 

Use ISynchronizeInvoke lugar de Control . Esa es la interfaz que Control implementa con Invoke/BeginInvoke/EndInvoke/InvokeRequired .

Una alternativa es usar SynchronizationContext.Current , que es lo que utiliza BackgroundWorker , creo.

Hoy en día es fácil invocar, por ejemplo, decir que queremos invocar una etiqueta (lblVal) para obtener el valor de txtVal

 lblVal.invoke((MethodInvoker)delegate{txtVal.Text = lblVal.Text;}); 

tan fácil como eso: D

Aquí está en VB.net, muy similar a la respuesta de Samuel. Tengo cuatro sobrecargas dependiendo de si quiere una subrutina o función y si hay o no un parámetro. Sería fácil agregar más sobrecargas para más parámetros. VB.Net es capaz de inferir los tipos.

 Module ISynchronizeInvokeExtensions Public Delegate Function GenericLambdaFunctionWithParam(Of InputType, OutputType)(ByVal input As InputType) As OutputType Private Delegate Function InvokeLambdaFunctionCallback(Of InputType, OutputType)(ByVal f As GenericLambdaFunctionWithParam(Of InputType, OutputType), ByVal input As InputType, ByVal c As System.ComponentModel.ISynchronizeInvoke) As OutputType Public Function InvokeEx(Of InputType, OutputType)(ByVal f As GenericLambdaFunctionWithParam(Of InputType, OutputType), ByVal input As InputType, ByVal c As System.ComponentModel.ISynchronizeInvoke) As OutputType If c.InvokeRequired Then Dim d As New InvokeLambdaFunctionCallback(Of InputType, OutputType)(AddressOf InvokeEx) Return DirectCast(c.Invoke(d, New Object() {f, input, c}), OutputType) Else Return f(input) End If End Function Public Delegate Sub GenericLambdaSubWithParam(Of InputType)(ByVal input As InputType) Public Sub InvokeEx(Of InputType)(ByVal s As GenericLambdaSubWithParam(Of InputType), ByVal input As InputType, ByVal c As System.ComponentModel.ISynchronizeInvoke) InvokeEx(Of InputType, Object)(Function(i As InputType) As Object s(i) Return Nothing End Function, input, c) End Sub Public Delegate Sub GenericLambdaSub() Public Sub InvokeEx(ByVal s As GenericLambdaSub, ByVal c As System.ComponentModel.ISynchronizeInvoke) InvokeEx(Of Object, Object)(Function(i As Object) As Object s() Return Nothing End Function, Nothing, c) End Sub Public Delegate Function GenericLambdaFunction(Of OutputType)() As OutputType Public Function InvokeEx(Of OutputType)(ByVal f As GenericLambdaFunction(Of OutputType), ByVal c As System.ComponentModel.ISynchronizeInvoke) As OutputType Return InvokeEx(Of Object, OutputType)(Function(i As Object) f(), Nothing, c) End Function End Module 

Uso (ejecuta esto en un backgroundworker):

  InvokeEx(Sub(x As String) Me.Text = x, "foo", Me) 'set form title to foo InvokeEx(AddressOf MsgBox, Me.Text, Me) InvokeEx(Sub() Me.Text &= "!", "foo", Me) 'append "!" to form title InvokeEx(AddressOf MsgBox, Me.Text, Me) Dim s As String = InvokeEx(Function() Me.Text, Me) & "bar" 'get form title to backgorundworker thread InvokeEx(AddressOf MsgBox, s, Me) 'display the string from backgroundworker thread 

aquí está el código equivalente de VB a la respuesta de Samuel que uso. note que en realidad tengo 2 funciones de extensiones, pero debo admitir que no sé por qué están ahí. Copié mi versión de C # hace años (tal vez de este sitio) y tenía ambas funciones de extensión, pero por qué razón, no entiendo completamente. Acabo de copiarlo y cómo usarlo, y medio entiendo todo lo que sucede bajo el capó con estas complicadas funciones.

 #Const System_ComponentModel = True #Const System_Drawing = False Option Compare Binary Option Explicit On Option Strict On Imports System.Collections Imports System.Runtime.CompilerServices ' for Extension() attribute Imports System.Text #If System_ComponentModel Then Imports System.ComponentModel #End If #If System_Drawing Then Imports System.Drawing #End If Public Module MyExtensions ' other #Region blocks are removed. i use many in my Extensions ' module/class. the below code is only the 2 relevant extension ' for this 'SafeInvoke' functionality. but i use other regions ' such as "String extensions" and "Numeric extensions". i use ' the above System_ComponentModel and System_Drawing compiler ' directives to include or exclude blocks of code that i want ' to either include or exclude in a project, which allows me to ' easily compare my code in one project with the same file in ' other projects to syncronise new changes across projects. ' you can scrap pretty much all the code above, ' but i'm giving it here so you see it in the full context. #Region "ISynchronizeInvoke extensions" #If System_ComponentModel Then  Public Function SafeInvoke(Of T As ISynchronizeInvoke, TResult)(isi As T, callFunction As Func(Of T, TResult)) As TResult If (isi.InvokeRequired) Then Dim result As IAsyncResult = isi.BeginInvoke(callFunction, New Object() {isi}) Dim endresult As Object = isi.EndInvoke(result) Return DirectCast(endresult, TResult) Else Return callFunction(isi) End If End Function '''  ''' This can be used in VB with: ''' txtMyTextBox.SafeInvoke(Sub(d) d.Text = "This is my new Text value.") ''' or: ''' txtMyTextBox.SafeInvoke(Sub(d) d.Text = myTextStringVariable) '''  '''  '''  '''  '''   Public Sub SafeInvoke(Of T As ISynchronizeInvoke)(isi As T, callFunction As Action(Of T)) If isi.InvokeRequired Then isi.BeginInvoke(callFunction, New Object() {isi}) Else callFunction(isi) End If End Sub #End If #End Region ' other #Region blocks are removed from here too. End Module 

Y la versión C # es:

 #define System_ComponentModel #undef System_Drawing using System; using System.Collections.Generic; using System.Linq; using System.Text; #if System_ComponentModel using System.ComponentModel; #endif #if System_Drawing using System.Drawing; #endif namespace MyCompany.Extensions { static partial class MyExtensions { // other #Region blocks are removed. i use many in my Extensions // module/class. the below code is only the 2 relevant extension // for this 'SafeInvoke' functionality. but i use other regions // such as "String extensions" and "Numeric extensions". i use // the above System_ComponentModel and System_Drawing compiler // directives to include or exclude blocks of code that i want // to either include or exclude in a project, which allows me to // easily compare my code in one project with the same file in // other projects to syncronise new changes across projects. // you can scrap pretty much all the code above, // but i'm giving it here so you see it in the full context. #region ISynchronizeInvoke extensions #if System_ComponentModel public static TResult SafeInvoke(this T isi, Func callFunction) where T : ISynchronizeInvoke { if (isi.InvokeRequired) { IAsyncResult result = isi.BeginInvoke(callFunction, new object[] { isi }); object endResult = isi.EndInvoke(result); return (TResult)endResult; } else return callFunction(isi); } ///  /// This can be used in C# with: /// txtMyTextBox.SafeInvoke(d => d.Text = "This is my new Text value."); /// or: /// txtMyTextBox.SafeInvoke(d => d.Text = myTextStringVariable); ///  ///  ///  ///  public static void SafeInvoke(this T isi, Action callFunction) where T : ISynchronizeInvoke { if (isi.InvokeRequired) isi.BeginInvoke(callFunction, new object[] { isi }); else callFunction(isi); } #endif #endregion // other #Region blocks are removed from here too. } // static class MyExtensions } // namespace 

Feliz encoding!