27

在本文中:

http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx

作者使用以下方法对 Windows 窗体控件进行线程安全调用:

private void SetText(string text)
{
    // InvokeRequired required compares the thread ID of the
    // calling thread to the thread ID of the creating thread.
    // If these threads are different, it returns true.
    if (this.textBox1.InvokeRequired)
    {    
        SetTextCallback d = new SetTextCallback(SetText);
        this.Invoke(d, new object[] { text });
    }
    else
    {
        this.textBox1.Text = text;
    }
}

有没有更短的方法来完成同样的事情?

4

5 回答 5

36

C# 3.0 及更高版本:

扩展方法通常是要走的路,因为您总是希望对ISynchronizeInvoke接口实现执行操作,这是一个很好的设计选择。

您还可以利用匿名方法(闭包)来解决您不知道将哪些参数传递给扩展方法的事实;关闭将捕获所需的一切状态。

// Extension method.
static void SynchronizedInvoke(this ISynchronizeInvoke sync, Action action)
{
    // If the invoke is not required, then invoke here and get out.
    if (!sync.InvokeRequired)
    {
        // Execute action.
        action();

        // Get out.
        return;
    }

    // Marshal to the required context.
    sync.Invoke(action, new object[] { });
}

然后你会这样称呼它:

private void SetText(string text)
{
    textBox1.SynchronizedInvoke(() => textBox1.Text = text);
}

在这里,闭包在text参数之上,该状态被捕获并作为传递给扩展方法的Action委托的一部分传递。

在 C# 3.0 之前:

您没有 lambda 表达式的奢侈,但您仍然可以概括代码。它几乎相同,但不是扩展方法:

static void SynchronizedInvoke(ISynchronizeInvoke sync, Action action)
{
    // If the invoke is not required, then invoke here and get out.
    if (!sync.InvokeRequired)
    {
        // Execute action.
        action();

        // Get out.
        return;
    }

    // Marshal to the required context.
    sync.Invoke(action, new object[] { });
}

然后你用匿名方法语法调用它:

private void SetText(string text)
{
    SynchronizedInvoke(textBox1, delegate() { textBox1.Text = text; });
}
于 2009-02-21T00:04:55.527 回答
10

1) 使用匿名委托

private void SetText(string text)
{
    if (this.InvokeRequired)
    {    
        Invoke(new MethodInvoker(delegate() {
            SetText(text);
        }));
    }
    else
    {
        this.textBox1.Text = text;
    }
}

2)AOP方法

[RunInUIThread]
private void SetText(string text)
{
    this.textBox1.Text = text;
}

http://weblogs.asp.net/rosherove/archive/2007/05.aspx?PageIndex=2

3) 使用 lambda 表达式(由其他人概述)。

于 2009-02-21T00:17:59.490 回答
6

编辑:我应该提到我不认为这是最佳实践

如果您使用的是 3.5,您可以制作一个扩展方法,效果如下:

public static void SafeInvoke(this Control control, Action handler) {
    if (control.InvokeRequired) {
        control.Invoke(handler);
    }
    else {
        handler();
    }
}

这基本上取自:这里

然后像这样使用它:

textBox1.SafeInvoke(() => .... );

当然,根据您的用途修改扩展名等。

于 2009-02-21T00:00:24.430 回答
1

这对大多数人来说可能很明显,但是如果您需要检索值,您可以采用接受的答案并添加另一种方法......

public static T SynchronizedFunc<T>(this ISynchronizeInvoke sync, Func<T> func)
{
    if (!sync.InvokeRequired)
    {
        // Execute the function
        return func();
    }

    // Marshal onto the context
    return (T) sync.Invoke(func, new object[] { });
}

我最近用它来以线程安全的方式处理表单......

var handle = f.SynchronizedFunc(() => f.Handle);
于 2015-01-07T14:44:29.020 回答
1

我找到的最短解决方案显示在下面的按钮示例中,目标是更改按钮的文本。

    if (buttonX.InvokeRequired)
        buttonX.Invoke((Action)(() => buttonX.Text = "Record"));
    else
        buttonX.Text = "Record";
于 2018-03-16T20:38:29.177 回答