Il modo migliore per richiamare qualsiasi codice cross-threaded?

So che questa domanda è stata posta prima, ma sto cercando un modo per:

  1. semplificare la creazione di un codice cross-thread sicuro.
  2. riutilizzare questo codice in qualsiasi situazione (senza riferimenti a Windows Form).

Ecco cosa ho finora, ma voglio rimuovere i riferimenti di Windows Form. Qualche idea?

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 suddetta class potrebbe essere utilizzata in questo modo:

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

Come rimuovere System.Windows.Forms.Control nella class SafeInvoke mantenendo la stessa funzionalità?

    Puoi anche utilizzare un metodo di estensione e lambda per rendere il tuo codice molto più pulito.

     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); } } } 

    Quindi ora puoi utilizzare InvokeEx su qualsiasi ISynchronizeInvoke ed essere in grado di accedere alle proprietà e ai campi della class di implementazione.

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

    Utilizzare ISynchronizeInvoke anziché Control . Questa è l’interfaccia che Control implementa con Invoke/BeginInvoke/EndInvoke/InvokeRequired .

    Un’alternativa è utilizzare SynchronizationContext.Current , che credo utilizzi BackgroundWorker .

    Ora un giorno è facile da richiamare, per esempio diciamo che vogliamo invocare un’etichetta (lblVal) per ottenere il valore di txtVal

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

    facile come quello: D

    Eccolo in VB.net, molto simile alla risposta di Samuel. Ho quattro sovraccarichi a seconda che si voglia una subroutine o una funzione e se ci sia o meno un parametro. Sarebbe facile aggiungere più sovraccarichi per più parametri. VB.Net è in grado di dedurre i tipi.

     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 

    Utilizzo (eseguilo in un worker di sfondo):

      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 

    ecco il codice equivalente VB della risposta di Samuel che uso. avviso ho effettivamente 2 funzioni di estensione, ma devo ammettere che non so perché sono lì. Ho copiato la mia versione C # anni fa (forse da questo sito) e aveva entrambe le funzioni di estensione, ma per quale motivo, non capisco completamente. l’ho appena copiato e come usarlo, e a metà capisco tutto ciò che accade sotto le casse con queste complicate funzioni.

     #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 

    E la versione C # è:

     #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 

    Buona programmazione!