我现在正在编写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的性能更差吗?或者我使用匿名函数是不好的做法?
答案 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线程上的所有回调(例如RunWorkerCompleted
和ProgressChanged
),这样您就不需要检查{{ 1}}。
答案 2 :(得分:2)
另一种做法:
Invoke((MethodInvoker)delegate
{
Label1.Text = "Foobar";
});