【问题标题】:Using MethodInvoker without Invoke在没有调用的情况下使用 MethodInvoker
【发布时间】:2012-08-14 07:33:06
【问题描述】:

我正在编写 GUI 应用程序有一段时间了,我一直使用的一件事是 MethodInvoker + lambda 函数来进行跨线程访问。

从我发现的示例中,我总是看到这样的东西:

版本 1

if (InvokeRequired)
{
    Invoke(new MethodInvoker(() => 
    {
        Label1.Text = "Foobar";
    });
}
else
{
    Label1.Text = "Foobar";
}

但是这会导致代码重复 --> 对我来说是个大坏蛋。

那么这有什么问题呢?

第 2 版

MethodInvoker updateText = new MethodInvoker(() => 
    {
        Label1.Text = "Foobar";
    });

if (InvokeRequired)
{
    Invoke(updateText);
}
else
{
    updateText();
}

现在我将功能捆绑在一个变量中,并在适当时使用 Invoke 或作为函数指针调用它。版本 2 性能更差吗?或者我为此使用匿名函数是不好的做法?

【问题讨论】:

    标签: c# user-interface multithreading lambda


    【解决方案1】:

    没有什么问题...但是您可以添加一个扩展方法以使其变得更好:

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

    然后你可以写:

    this.InvokeIfNecessary(() => Label1.Text = "Foobar");
    

    更整洁:)

    当您不需要时创建委托会有非常轻微的性能缺陷,但这几乎可以肯定是微不足道的 - 专注于编写干净的代码。

    请注意,即使您不想这样做,您仍然可以在现有代码中简化变量声明:

    MethodInvoker updateText = () => Label1.Text = "Foobar";
    

    这是使用单独变量的一个好处 - 您不需要 new MethodInvoker 位来告诉 lambda 表达式您想要什么类型的委托...

    【讨论】:

    • 很好,我认为我有 2 个方面 :-) 不知道更短的 Lambda 选项。另外,我认为扩展方法更加优雅。它使我能够更加强调我正在做的事情(InvokeIfRequired)。这有必要将第二个参数提供给 Invoke 吗?至少 'Form' 有一个 arg 重载?
    • @Nebula:不,看起来你是对的。我首先查看的是更通用的ISynchronizeInvoke 版本。将针对这种情况进行编辑。
    • 好的,谢谢。我刚才实际上已经测试了这种方法,我对此非常满意。它使代码干净整洁。我添加了一个 control.IsDisposedcontrol.Disposing 守卫,使其值得生产,再次感谢!
    • 从头开始,添加 control.IsDisposed 和 control.Disposing 不会增加任何安全性,我会在别处解决提前关闭表单的问题。
    • 您能看看stackoverflow.com/questions/16961404/… 并指出正确的方向吗?
    【解决方案2】:

    版本 2 的性能是否更差?或者我为此使用匿名函数是不好的做法?

    没有版本 2 更好,不用担心它的性能问题。除了使用匿名函数,您还可以定义一个方法:

    public void SetLabelTextToFooBar()
    {
        Label1.Text = "Foobar";
    }
    

    然后:

    if (InvokeRequired)
    {
        Invoke(SetLabelTextToFooBar);
    }
    else
    {
        SetLabelTextToFooBar();
    }
    

    或者简单地使用BackgroundWorker,它将在主 UI 线程上自动执行所有回调(例如RunWorkerCompletedProgressChanged),这样您就不需要检查InvokeRequired

    【讨论】:

    • 对于这个例子我同意你的看法。但是,当您有一个更真实的示例时,小型包装器方法的数量往往会增长得很快。例如,当我使用后台工作人员进行大量 UI 更新时,我倾向于尽可能减少 Invoke 中的工作量。这意味着我必须创建很多函数才能做到这一点,随着时间的推移会变得晦涩难懂。
    【解决方案3】:

    另一种做法:

    Invoke((MethodInvoker)delegate 
    {
         Label1.Text = "Foobar";
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-14
      • 2013-06-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多