MethodInvoker和Action在Control.BeginInvoke中的区别

67

哪个更正确,为什么?

Control.BeginInvoke(new Action(DoSomething), null);

private void DoSomething()
{
    MessageBox.Show("What a great post");
}
或者
Control.BeginInvoke((MethodInvoker) delegate { 
    MessageBox.Show("What a great post");
}); 

我觉得我正在做同样的事情,那么什么时候使用MethodInvoker与Action或编写lambda表达式才是合适的时间呢?

编辑: 我知道编写lambda与Action之间并没有太大的区别,但MethodInvoker似乎是为特定目的而制作的。它是否有任何不同之处?


请查看这个链接:http://mark-dot-net.blogspot.com.uy/2014/07/six-ways-to-initiate-tasks-on-another.html - NoWar
7个回答

87

两种方法都是正确的,但Control.Invoke文档指出:

委托可以是 EventHandler 的实例,在这种情况下,sender 参数将包含此控件,event 参数将包含 EventArgs.Empty。委托还可以是 MethodInvoker 或任何采用 void 参数列表的其他委托类型。调用 EventHandler 或 MethodInvoker 委托将比调用其他类型的委托更快。

因此,MethodInvoker 将是更高效的选择。


谢谢Jon,但是什么使MethodInvoker比没有参数的Action调用更有效? - Mike_G
32
简而言之,它似乎使用is/as检查来检查这两个特殊情况,并使用Invoke而不是DynamicInvoke——后者速度显著更快。 - Marc Gravell

31

以下每个解决方案我都运行了131072(128 * 1024)次迭代(在一个单独的线程中)。

  • 只读 MethodInvoker:5664.53(+0%)
  • 新的 MethodInvoker:5828.31(+2.89%)
  • MethodInvoker 中进行函数转换:5857.07 (+3.40%)
  • 只读 Action:6467.33(+14.17%)
  • 新的 Action:6829.07(+20.56%)

在每次迭代中调用一个新的 Action

    private void SetVisibleByNewAction()
    {
        if (InvokeRequired)
        {
            Invoke(new Action(SetVisibleByNewAction));
        }
        else
        {
            Visible = true;
        }
    }

每次迭代调用只读内置构造函数Action

    // private readonly Action _actionSetVisibleByAction
    // _actionSetVisibleByAction= SetVisibleByAction;
    private void SetVisibleByAction()
    {
        if (InvokeRequired)
        {
            Invoke(_actionSetVisibleByAction);
        }
        else
        {
            Visible = true;
        }
    }

在每次迭代中调用一个新的MethodInvoker

    private void SetVisibleByNewMethodInvoker()
    {
        if (InvokeRequired)
        {
            Invoke(new MethodInvoker(SetVisibleByNewMethodInvoker));
        }
        else
        {
            Visible = true;
        }
    }

在每次迭代中调用只读、内置构造函数MethodInvoker

    // private readonly MethodInvoker _methodInvokerSetVisibleByMethodInvoker 
    // _methodInvokerSetVisibleByMethodInvoker = SetVisibleByMethodInvoker;
    private void SetVisibleByMethodInvoker()
    {
        if (InvokeRequired)
        {
            Invoke(_methodInvokerSetVisibleByMethodInvoker);
        }
        else
        {
            Visible = true;
        }
    }

在每次迭代中调用MethodInvoker函数进行转换

    private void SetVisibleByDelegate()
    {
        if (InvokeRequired)
        {
            Invoke((MethodInvoker) SetVisibleByDelegate);
        }
        else
        {
            Visible = true;
        }
    }

呼叫“新行动”解决方案的示例:

    private void ButtonNewActionOnClick(object sender, EventArgs e)
    {
        new Thread(TestNewAction).Start();
    }

    private void TestNewAction()
    {
        var watch = Stopwatch.StartNew();
        for (var i = 0; i < COUNT; i++)
        {
            SetVisibleByNewAction();
        }
        watch.Stop();
        Append("New Action: " + watch.ElapsedMilliseconds + "ms");
    }

你如何阅读这个结果?我对性能分析很新。提供一个链接会很有帮助。 - Gaurav

13

我更喜欢使用Lambda表达式和Actions/Funcs:

Control.BeginInvoke(new Action(() => MessageBox.Show("What a great post")));

9

Action在System中定义,而MethodInvoker在System.Windows.Forms中定义 - 你最好使用Action,因为它可移植到其他地方。你还会发现更多的地方接受Action作为参数,而不是MethodInvoker。

然而,文档确实指出,在Control.Invoke()中调用EventHandler或MethodInvoker类型的委托将比其他任何类型都更快。

除了它们所在的命名空间之外,我认为Action和MethodInvoker之间没有实质性的功能差异 - 它们本质上都被定义为:

public delegate void NoParamMethod();

作为额外信息,Action有几种可传入参数的重载形式 - 这是泛型的,因此它们可以是类型安全的。

3
根据MSDN文档,MethodInvoker提供了一个简单的委托,用于调用void参数列表的方法。当调用控件的Invoke方法或者需要一个简单的委托但不想自己定义时,可以使用此委托。
另一方面,Action可以带有最多4个参数。
但我认为MethodInvoker和Action之间没有任何区别,因为它们都封装了一个不带参数且返回void的委托。
如果查看它们的定义,您将只会看到这个内容。
public delegate void MethodInvoker();
public delegate void Action();

顺便说一下,你也可以将第二行写成以下形式。

Control.BeginInvoke(new MethodInvoker(DoSomething), null);

Control.BeginInvoke(new MethodInvoker(DoSomething)); 或者 Control.BeginInvoke(new MethodInvoker(() => { DoSomething(); })); - juFo
根据文档,Action 可以接受最多 16 个参数。https://learn.microsoft.com/en-us/dotnet/api/system.action-16?view=netframework-4.5 - Jim Fell

2

在大多数情况下,这是一个偏好的问题,除非您打算重新使用DoSomething()方法。另外,匿名函数会将作用域变量放置在堆上,可能使它成为一个更昂贵的函数。


1

不要忘记在关闭表单时检查控件是否可用,以避免错误发生。

if(control.IsHandleCreated)
control.BeginInvoke((MethodInvoker)(() => control.Text="check123"));

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