代码之家  ›  专栏  ›  技术社区  ›  Tom Corelis

自动调用所需的代码模式

  •  170
  • Tom Corelis  · 技术社区  · 14 年前

    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.visible = true

    以前的 answers 仍然 很难处理。

    9 回复  |  直到 7 年前
        1
  •  145
  •   Olivier Jacot-Descombes    8 年前

    李的方法可以进一步简化

    public static void InvokeIfRequired(this Control control, MethodInvoker action)
    {
        // See Update 2 for edits Mike de Klerk suggests to insert here.
    
        if (control.InvokeRequired) {
            control.Invoke(action);
        } else {
            action();
        }
    }
    

    可以这样称呼

    richEditControl1.InvokeIfRequired(() =>
    {
        // Do anything you want with the control here
        richEditControl1.RtfText = value;
        RtfHelpers.AddMissingStyles(richEditControl1);
    });
    

    closure .


    更新 :

    Control 可以概括为 ISynchronizeInvoke :

    public static void InvokeIfRequired(this ISynchronizeInvoke obj,
                                             MethodInvoker action)
    {
        if (obj.InvokeRequired) {
            var args = new object[0];
            obj.Invoke(action, args);
        } else {
            action();
        }
    }
    

    这个 Invoke 方法作为 action .


    更新2

    // When the form, thus the control, isn't visible yet, InvokeRequired  returns false,
    // resulting still in a cross-thread exception.
    while (!control.Visible)
    {
        System.Threading.Thread.Sleep(50);
    }
    

    请参阅下面ToolmakerSteve的评论,了解对该建议的关注。

        2
  •  135
  •   Lee    14 年前

    您可以编写一个扩展方法:

    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
    
        3
  •  38
  •   Michael Pakhantsov    11 年前

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

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

    我是根据这篇博文写的 here InvokeRequired 财产。

        4
  •  10
  •   Aaron Gage    14 年前

    创建一个ThreadSafeInvoke.snippet文件,然后只需选择更新语句,右键单击并选择“环绕…”或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>
    
        5
  •  8
  •   gxtaillon    9 年前

    这是李、奥利弗和斯蒂芬答案的改进版/组合版。

    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;
    });
    
        6
  •  5
  •   stephan Schmuck    11 年前

    这在我开始使用单个全局实例委托时没有发生。

    delegate void ShowMessageCallback(string message);
    
    private void Form1_Load(object sender, EventArgs e)
    {
        ShowMessageCallback showMessageDelegate = new ShowMessageCallback(ShowMessage);
    }
    
    private void ShowMessage(string message)
    {
        if (this.InvokeRequired)
            this.Invoke(showMessageDelegate, message);
        else
            labelMessage.Text = message;           
    }
    
    void Message_OnMessage(object sender, Utilities.Message.MessageEventArgs e)
    {
        ShowMessage(e.Message);
    }
    
        7
  •  5
  •   Konstantin S.    6 年前

    control.InvokeIfRequired(c => c.Visible = false);
    
    return control.InvokeIfRequired(c => {
        c.Visible = value
    
        return c.Visible;
    });
    

    代码:

    using System;
    using System.ComponentModel;
    
    namespace Extensions
    {
        public static class SynchronizeInvokeExtensions
        {
            public static void InvokeIfRequired<T>(this T obj, Action<T> action)
                where T : ISynchronizeInvoke
            {
                if (obj.InvokeRequired)
                {
                    obj.Invoke(action, new object[] { obj });
                }
                else
                {
                    action(obj);
                }
            }
    
            public static TOut InvokeIfRequired<TIn, TOut>(this TIn obj, Func<TIn, TOut> func) 
                where TIn : ISynchronizeInvoke
            {
                return obj.InvokeRequired
                    ? (TOut)obj.Invoke(func, new object[] { obj })
                    : func(obj);
            }
        }
    }
    
        8
  •  2
  •   Walter Verhoeven    8 年前

    我喜欢做一些不同的事情,如果需要的话,我喜欢用行动来称呼“我自己”,

        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的字段,因为可能有一些后台线程仍在运行。。。

        9
  •  -3
  •   Steve Wood    10 年前

    您永远不应该编写这样的代码:

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

    如果您确实有这样的代码,那么您的应用程序就不是线程安全的。这意味着您的代码已经从另一个线程调用了DoGUISwitch()。现在检查它是否在另一个线程中已经太晚了。在调用DoGUISwitch之前,必须先调用InvokeRequest。您不应该从其他线程访问任何方法或属性。

    Control.InvokeRequired Property

    除了InvokeRequired属性外,上还有四个方法 创建。