VB .Net 到 C# 函数,用于控制跨线程

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

我正在尝试从 VB 切换。 Net 到 C#(.Net 框架和 .Net &),所以我开始“翻译”一些我常用的函数。但是我遇到了一些问题(我对基于 C 的语言一无所知)。

所以,这是我使用了多年的工作 VB .Net 函数,当我从不同的线程、任务等调用控制时,它是一个简单的跨线程解决方案:

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

我都这样称呼他们:

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

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

将它翻译成 C# 后我得到了这个:

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

这里,在这一行 return (T)F.Compile().DynamicInvoke(); 中,我遇到了有关 Null 变量转换的错误。在 VB .Net 中,我使用了 C# 中似乎不存在的 Directcast。 但主要问题是,我怎么称呼他们?因为这个,给我 2 个不同的错误:

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

            );
        }

在任务中,第一行 (Txt_Stato) 给我“InvocaMetodoSicuro 不是 ToolStripLabel 的一部分”。因为它不能从 stati class 获取扩展名。第二行(WW2 是 WebView2)给我“表达式树可能不包含赋值运算符。” 第三行(string str ...)没有给我任何错误,但我不知道是否有效,因为静态 class.

中的“空错误”导致调试无法启动

同样,我正在尝试通过自学从 VB .Net 切换到 C#,并且我想转换我在 VB .Net 中拥有的一些有用功能。所以,如果 C# 是一团糟,请认为我 2 小时前才开始用它编写代码。 那么,代码中的问题是什么?

我不知道你为什么使用 Expression<Action>Expression<Func<T>>ActionFunc<T> 就足够了。您没有以任何有意义的方式使用 Expression<>

您的代码可以简单地重写为:

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

您甚至可以 re-use 使用相同的名称(就像我所做的那样),因为方法重载可以为您解决所有问题。

我建议更进一步并添加以下签名:

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

这些版本是针对调用控件调用的,使重构更安全一些,并减少了重复。

例如,这个:

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

...变成:

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

以上代码在 .NET 6.0 中运行良好,因为在核心中添加了新的 Invoke 重载。

对于 4.7.2,您需要以下代码:

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