在本文中:
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;
}
}
有没有更短的方法来完成同样的事情?
答案 0 :(得分:34)
C#3.0以及之后:
扩展方法通常是要走的路,因为你总是希望对ISynchronizeInvoke
interface实现执行操作,这是一个很好的设计选择。
您还可以利用anonymous methods(闭包)来解释您不知道要传递给扩展方法的参数的事实;关闭将捕获所需的一切状态。
// 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
delegate的一部分传递。
在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; });
}
答案 1 :(得分:8)
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表达式(由他人概述)。
答案 2 :(得分:4)
编辑:我应该提到我不认为这是最佳实践
如果您使用的是3.5,则可以使用扩展方法:
public static void SafeInvoke(this Control control, Action handler) {
if (control.InvokeRequired) {
control.Invoke(handler);
}
else {
handler();
}
}
这基本上来自:Here
然后使用它:
textBox1.SafeInvoke(() => .... );
当然要为您的用法修改扩展名等。
答案 3 :(得分: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);
答案 4 :(得分:0)
我找到的最短解决方案显示在下面的按钮示例中,其目标是更改按钮的文本。
if (buttonX.InvokeRequired)
buttonX.Invoke((Action)(() => buttonX.Text = "Record"));
else
buttonX.Text = "Record";