Home > Blockchain >  VB .Net to C# function for controls cross-thread
VB .Net to C# function for controls cross-thread

Time:02-22

i'm trying to switch from VB. Net to C# (both .Net framework and .Net &) so i started to "translate" some function that i use quite common. But i'm facing some problems (i have zero clue in C based languages).

So, this is the working VB .Net function i used for years, is a simple cross-thread solution when i call control from a different thread, task, etc.:

Imports System.Linq.Expressions
Imports System.Runtime.CompilerServices

Module Module1

    Private Delegate Sub InvokeThreadSafeMethodDelegate(ByVal Cnt As Control, ByVal Mtd As Expression(Of Action))
    Private Delegate Function InvokeThreadSafeFunctionDelegate(Of T)(ByVal Cnt As Control, ByVal Fnc As Expression(Of Func(Of T))) As T


    <Extension()>
    Public Sub InvokeThreadSafeMethod(ByVal Cnt As Control, ByVal Mtd As Expression(Of Action))
        If (Cnt.InvokeRequired) Then
            Dim Dlg = New InvokeThreadSafeMethodDelegate(AddressOf InvokeThreadSafeMethod)
            Cnt.Invoke(Dlg, Cnt, Mtd)
        Else
            Mtd.Compile().DynamicInvoke()
        End If
    End Sub


    <Extension()>
    Public Function InvokeThreadSafeFunction(Of T)(ByVal Cnt As Control, ByVal Fnc As Expression(Of Func(Of T))) As T
        If (Cnt.InvokeRequired) Then
            Dim Dlg = New InvokeThreadSafeFunctionDelegate(Of T)(AddressOf InvokeThreadSafeFunction)
            Return DirectCast(Cnt.Invoke(Dlg, Cnt, Fnc), T)
        Else
            Return DirectCast(Fnc.Compile().DynamicInvoke(), T)
        End If
    End Function

End Module

And i call them both with that:

Me.Label1.InvokeThreadSafeMethod(Sub() Me.Label1.text = "Test")

Dim X as String = Me.Label1.InvokeThreadSafeFunction(Function() Me.Label1.text)

After translateing it to C# i got this:

using System.Linq.Expressions;

internal static class Cls_Comune
{

    private delegate void InvokeThreadSafeMethodDelegate(Control C, Expression<Action> M);
    private delegate T InvokeThreadSafeFunctionDelegate<T>(Control C, Expression<Func<T>> F);

    public static void InvocaMetodoSicuro(this Control C, Expression<Action> M)
    {
        if (C.InvokeRequired)
        {
            var D = new InvokeThreadSafeMethodDelegate(InvocaMetodoSicuro);
            C.Invoke(D, C, M);
        }
        else
            M.Compile().DynamicInvoke();
    }

    public static T InvocaFunzioneSicuro<T>(this Control C, Expression<Func<T>> F)
    {
        if (C.InvokeRequired)
        {
            var D = new InvokeThreadSafeFunctionDelegate<T>(InvocaFunzioneSicuro);
            return (T)C.Invoke(D, C, F);
        }
        else
            return (T)F.Compile().DynamicInvoke();
    }
}

Here, in this line return (T)F.Compile().DynamicInvoke(); i got an error about the conversion of Null variables. In VB .Net i have used a Directcast that seems to not be present in C#. But the main problem is, how i can call them? Because this, give me 2 different errors:

private async void Naviga(string strLnk)
        {
            await Task.Run(void () =>
            {
                Txt_Stato.InvocaMetodoSicuro(() => Txt_Stato.Text = "Test");

                WW2.InvocaMetodoSicuro(() => WW2.Source = new System.Uri(strLnk, UriKind.Absolute));

                string str = WW2.InvocaFunzioneSicuro(() => WW2.Source.ToString());
            }

            );
        }

In the Task, the first line (Txt_Stato) give me "InvocaMetodoSicuro is not a part of ToolStripLabel". As it can't take the extension from the stati class. The second line (WW2 is a WebView2) give me "An expression tree may not contain an assignment operator." The third line (string str...) give me no errors, but i dont know if works because the debug wont start due to the "Null error" in the static class.

Again, i'm trying with self-learning to switch from VB .Net to C# and i want to convert some useful functions that i had in VB .Net. So, if the C# is a mess, think that i just started to code with it 2 hours ago. So, what is the problem in the code?

CodePudding user response:

I don't know why you're using Expression<Action> and Expression<Func<T>> when Action and Func<T> would suffice. You're not using Expression<> in any meaningful way.

Your code can simply be rewritten as this:

public static void InvocaSicuro(this Control C, Action M)
{
    if (C.InvokeRequired)
        C.Invoke(M);
    else
        M();
}

public static T InvocaSicuro<T>(this Control C, Func<T> F) =>
    C.InvokeRequired
    ? C.Invoke(F)
    : F();

You can even re-use the same name (as I have done) as method overloading can figure it all out for you.

I would suggest going one step further and adding the following signatures:

public static void InvocaSicuro<C>(this C control, Action<C> action) where C : Control
{
    if (control.InvokeRequired)
        control.Invoke(() => action(control));
    else
        action(control);
}

public static T InvocaSicuro<C, T>(this C control, Func<C, T> func) where C : Control =>
    control.InvokeRequired
    ? control.Invoke(() => func(control))
    : func(control);

These versions are invoked against the calling control and make refactoring a little safer and it reduces repetition.

For example, this:

Txt_Stato.InvocaSicuro(() => Txt_Stato.Text = "Test");

...becomes:

Txt_Stato.InvocaSicuro(c => c.Text = "Test");

The above code works fine in .NET 6.0 as new Invoke overloads were added in core.

For 4.7.2 you need the following code:

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

public static T InvocaSicuro<T>(this Control control, Func<T> func) =>
    control.InvokeRequired
    ? (T)control.Invoke((Delegate)func)
    : func();

public static void InvocaSicuro<C>(this C control, Action<C> action) where C : Control
{
    if (control.InvokeRequired)
        control.Invoke((Action)(() => action(control)));
    else
        action(control);
}

public static T InvocaSicuro<C, T>(this C control, Func<C, T> func) where C : Control =>
    control.InvokeRequired
    ? (T)control.Invoke((Action)(() => func(control)))
    : func(control);
  • Related