Кратчайший способ написать потокобезопасный метод доступа к элементу управления Windows Form
В этой статье:
http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx
Автор использует следующий метод для выполнения поточно-ориентированных вызовов элемента управления Windows Forms:
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;
}
}
Есть ли более короткий способ сделать то же самое?
5 ответов
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:
У вас нет роскоши лямбда-выражений, но вы все равно можете обобщить код. Это почти то же самое, но не метод расширения:
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) Использование анонимного делегата
private void SetText(string text)
{
if (this.InvokeRequired)
{
Invoke(new MethodInvoker(delegate() {
SetText(text);
}));
}
else
{
this.textBox1.Text = text;
}
}
2) АОП подход
[RunInUIThread]
private void SetText(string text)
{
this.textBox1.Text = text;
}
http://weblogs.asp.net/rosherove/archive/2007/05.aspx?PageIndex=2
3) Использование лямбда-выражений (обрисовано в общих чертах другими).
Изменить: я должен отметить, что я не считаю это лучшей практикой
Если вы используете 3.5, вы можете сделать метод расширения с эффектом:
public static void SafeInvoke(this Control control, Action handler) {
if (control.InvokeRequired) {
control.Invoke(handler);
}
else {
handler();
}
}
это в основном взято из: Здесь
Тогда используйте это как:
textBox1.SafeInvoke(() => .... );
Конечно, измените расширение и т. Д. Для вашего использования.
Это может быть очевидно для большинства, но вы можете взять принятый ответ и добавить другой метод, если вам нужно получить значение...
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);
Самое короткое решение, которое я нашел, показано в примере кнопки ниже, где цель состоит в том, чтобы изменить текст кнопки.
if (buttonX.InvokeRequired)
buttonX.Invoke((Action)(() => buttonX.Text = "Record"));
else
buttonX.Text = "Record";