Самый короткий способ написать потокобезопасный метод доступа к управлению формами окон

В этой статье:

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;
    }
}

Есть ли более короткий способ выполнить одно и то же?

Ответ 1

С# 3.0 и после:

Обычно используется метод расширения, поскольку вам всегда нужно выполнить действие на ISynchronizeInvoke interface реализация, это хороший выбор дизайна.

Вы также можете воспользоваться анонимными методами (закрытие), чтобы учесть тот факт, что вы не знаете, какие параметры должны пройти метод расширения; закрытие будет фиксировать состояние всего необходимого.

// 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, переданное метод расширения.

До С# 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; });
}

Ответ 2

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) Использование лямбда-выражений (обозначенных другими). ​​

Ответ 3

Изменить: я должен упомянуть, что не считаю это лучшей практикой

Если вы используете 3.5, вы можете сделать метод расширения эффектом:

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

это в основном взято из: Здесь

Затем используйте его как:

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

Конечно, измените расширение и т.д. для ваших обычаев.

Ответ 4

Это может быть очевидно для большинства, но вы можете принять принятый ответ и добавить другой метод, если вам нужно получить значение...

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);