我已经痛苦地意识到,在事件驱动的GUI代码中,人们需要多么频繁地编写以下代码模式

private void DoGUISwitch() {
    // cruisin for a bruisin' through exception city
    object1.Visible = true;
    object2.Visible = false;
}

就变成:

private void DoGUISwitch() {
    if (object1.InvokeRequired) {
        object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); }));
    } else {
        object1.Visible = true;
        object2.Visible = false;
    }
}

这在c#中是一种尴尬的模式,无论是记忆还是输入。有没有人想出某种捷径或构造来在一定程度上自动化这个?如果可以像object1.InvokeIfNecessary一样,将函数附加到对象上,无需执行所有这些额外的工作就可以进行检查,那就太棒了。可见= true类型快捷方式。

前面的回答讨论了每次只调用Invoke()的不可行性,即使这样,Invoke()语法也是低效的,而且处理起来仍然很尴尬。

有人找到什么捷径了吗?


当前回答

我喜欢做的有点不同,如果需要一个动作,我喜欢叫“我自己”,

    private void AddRowToListView(ScannerRow row, bool suspend)
    {
        if (IsFormClosing)
            return;

        if (this.InvokeRequired)
        {
            var A = new Action(() => AddRowToListView(row, suspend));
            this.Invoke(A);
            return;
        }
         //as of here the Code is thread-safe

这是一个方便的模式,IsFormClosing是一个字段,当我关闭我的表单时,我设置为True,因为可能有一些后台线程仍在运行…

其他回答

你可以写一个扩展方法:

public static void InvokeIfRequired(this Control c, Action<Control> action)
{
    if(c.InvokeRequired)
    {
        c.Invoke(new Action(() => action(c)));
    }
    else
    {
        action(c);
    }
}

像这样使用它:

object1.InvokeIfRequired(c => { c.Visible = true; });

编辑:正如Simpzon在评论中指出的那样,你也可以将签名更改为:

public static void InvokeIfRequired<T>(this T c, Action<T> action) 
    where T : Control

以下是李、奥利弗和斯蒂芬回答的改进版/综合版。

public delegate void InvokeIfRequiredDelegate<T>(T obj)
    where T : ISynchronizeInvoke;

public static void InvokeIfRequired<T>(this T obj, InvokeIfRequiredDelegate<T> action)
    where T : ISynchronizeInvoke
{
    if (obj.InvokeRequired)
    {
        obj.Invoke(action, new object[] { obj });
    }
    else
    {
        action(obj);
    }
} 

模板允许灵活和无强制转换的代码,这是更可读的,而专用委托提供了效率。

progressBar1.InvokeIfRequired(o => 
{
    o.Style = ProgressBarStyle.Marquee;
    o.MarqueeAnimationSpeed = 40;
});

这是我在所有代码中一直使用的表单。

private void DoGUISwitch()
{ 
    Invoke( ( MethodInvoker ) delegate {
        object1.Visible = true;
        object2.Visible = false;
    });
} 

我是根据这篇博客文章得出的结论。这种方法没有让我失败过,所以我认为没有理由用检查InvokeRequired属性来使我的代码复杂化。

希望这能有所帮助。

我喜欢做的有点不同,如果需要一个动作,我喜欢叫“我自己”,

    private void AddRowToListView(ScannerRow row, bool suspend)
    {
        if (IsFormClosing)
            return;

        if (this.InvokeRequired)
        {
            var A = new Action(() => AddRowToListView(row, suspend));
            this.Invoke(A);
            return;
        }
         //as of here the Code is thread-safe

这是一个方便的模式,IsFormClosing是一个字段,当我关闭我的表单时,我设置为True,因为可能有一些后台线程仍在运行…

创建一个ThreadSafeInvoke。然后你可以选择更新语句,右键单击并选择Surround With…'或Ctrl-K+S:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippet Format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <Header>
    <Title>ThreadsafeInvoke</Title>
    <Shortcut></Shortcut>
    <Description>Wraps code in an anonymous method passed to Invoke for Thread safety.</Description>
    <SnippetTypes>
      <SnippetType>SurroundsWith</SnippetType>
    </SnippetTypes>
  </Header>
  <Snippet>
    <Code Language="CSharp">
      <![CDATA[
      Invoke( (MethodInvoker) delegate
      {
          $selected$
      });      
      ]]>
    </Code>
  </Snippet>
</CodeSnippet>