为什么在这段代码中要使用Action?

12

您好,我看到了以下代码:

void UpdateMessage (string message)
{
    Action action = () => txtMessage.Text = message;
    this.Invoke (action);
}
为什么要在这里使用Action然后再调用Action?为什么不直接使用txtMessage.Text = message替换函数体中的代码?

更新

在评论中呈现了该代码的完整版本,下面复制并加上语法高亮、缩进等。

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
        InitializeComponent(); 
        new Thread(Work).Start(); 
    } 

    void Work() 
    { 
        Thread.Sleep(5000); 
        UpdateMessage("My Garden"); 
    } 

    void UpdateMessage(string message) { 
        Action action = () => textBox1.Text = message; 
        this.Invoke(action); 
    } 
} 

1
我认为你所遇到的是我们业界所谓的“烂代码”。它可能是从系统中某个实际需要间接引用的地方复制而来。但你是100%正确的,这并不需要。 - Axeman
3
@Axeman,这不是“糟糕的代码”,而是 UI 线程调用。 - Viacheslav Smityukh
1
不要将其与 action.Invoke() 调用混淆。 - Viacheslav Smityukh
@Axeman,那些只是Windows的规则。你有什么替代方案吗? - David Heffernan
@Axeman,一旦你熟悉了这个模式,它就非常简单。 - David Heffernan
显示剩余3条评论
3个回答

20

这段代码在不同的线程上运行,必须使用 Invoke 跨越到UI线程。

Control.Invoke()文档说明如下:

在拥有控件基础窗口句柄的线程上执行指定的委托。

所有这些都是必需的,因为底层的Windows框架要求对窗口句柄的操作必须由拥有该窗口句柄的线程执行。


12

如果UpdateMessage是从另一个线程调用的,您需要在调用GUI元素之前进入主线程。

如果您只使用txtMessage.Text = message,则会出现CrossThreadOperationException


是的,这个UpdateMessage被调用在除了主UI线程之外的另一个线程中。但是为什么我使用txtMessage.Text = message却没有看到应该出现的异常呢?谢谢! - spspli

4

如果您在UI线程之外操作控件的属性,可能会收到异常。

Control.Invoke()方法通过向窗口消息循环发送消息来执行委托。

但是,当不需要进行线程同步时,可以优化代码以避免不必要的线程同步:

void UpdateMessage (string message)
{
    if(InvokeRequired)
    {
        Invoke((Action)()=>UpdateMessage(message));
        return;
    }

    txtMessage.Text = message;
}

使用 System; 使用 System.Collections.Generic; 使用 System.ComponentModel; 使用 System.Data; 使用 System.Drawing; 使用 System.Linq; 使用 System.Text; 使用 System.Windows.Forms; 使用 System.Threading;namespace TestAction { public partial class Form1 : Form { public Form1() { InitializeComponent(); new Thread(Work).Start(); } void Work() { Thread.Sleep(5000); UpdateMessage("我的花园"); } void UpdateMessage(string message) { Action action = () => textBox1.Text = message; this.Invoke(action); } } } - spspli
1
在这段代码中,你从构造函数启动的线程中调用UpdateMessage方法。Control.Invoke()方法在当前情况下是必要的。 - Viacheslav Smityukh
谢谢!我知道现在这是必要的。但我只是想知道,如果我不使用它,为什么我没有看到任何异常。 - spspli
尝试在调试器下启动它,你应该能看到那个异常。 - Viacheslav Smityukh

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