使用不带Invoke的MethodInvoker

时间:2012-08-14 07:33:07

标签: c# user-interface multithreading lambda

我现在正在编写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的性能更差吗?或者我使用匿名函数是不好的做法?

3 个答案:

答案 0 :(得分:10)

它没有什么问题......但是你可以添加一个扩展方法来使它更好一些:

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表达式您想要什么类型的委托......

答案 1 :(得分:2)

  

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

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

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

然后:

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

或者只是使用BackgroundWorker,它会自动执行主UI线程上的所有回调(例如RunWorkerCompletedProgressChanged),这样您就不需要检查{{ 1}}。

答案 2 :(得分:2)

另一种做法:

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