使用MethodInvoker而不带Invoke

4

我已经写了一段时间的图形用户界面应用程序,我总是使用MethodInvoker + lambda函数来进行跨线程访问。

从我找到的示例中,我总是看到像这样的代码:

版本1

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

然而这导致了代码重复——对我来说是一个很大的问题。

那么这有什么问题吗?

第二版

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

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

现在我把功能捆绑在一个变量中,在适当的时候使用Invoke或函数指针调用它。版本2在性能方面更差吗?或者使用匿名函数是不好的做法吗?
3个回答

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


很好,学到了两点:-) 我不知道还有更短的Lambda选项。此外,我认为扩展方法更加优雅。它使我能够更加强调我正在做什么(InvokeIfRequired)。但是,必须给Invoke传递第二个参数吗?至少"Form"有一个单参数重载? - Nebula
@Nebula:不对,看起来你是正确的。我先看了更一般的“ISynchronizeInvoke”版本。会为这种情况进行编辑。 - Jon Skeet
好的,谢谢。我刚刚测试了这个方法,并且非常满意。它使代码更加清晰。我已经添加了 control.IsDisposedcontrol.Disposing 的防护措施,使其适用于生产环境,再次感谢! - Nebula
划掉那个,添加 control.IsDisposed 和 control.Disposing 不会增加任何安全性,我会在其他地方解决我的窗体意外关闭问题。 - Nebula
请问您能否查看http://stackoverflow.com/questions/16961404/invalideoperationexception-invoke#16961404并指引我正确的方向? - Offler

2

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

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

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

然后:

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

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


对于这个例子,我同意你的观点。然而,在更多真实场景下,小包装器方法的数量往往会迅速增长。例如,当我使用后台工作者来进行大量用户界面更新时,我往往会尽可能保持在一个Invoke中的工作量最小。这意味着我需要编写许多函数来完成此任务,随着时间的推移,这些函数往往会变得晦涩难懂。 - Nebula

2

另一种做法:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接