MethodInvoker + лямбда + аргументы + операция CrossThread

Я использую это, чтобы что-то изменить в другом потоке:

        MethodInvoker m = () => { login_submit.Text = "Login"; };
        if (InvokeRequired)
        {
            BeginInvoke(m);
        }
        else
        {
            Invoke(m);
        }

это работает нормально.

Как я могу передать аргументы этому лямба-выражению?

Я хочу сделать что-то вроде этого:

        MethodInvoker m = (string txt) => { login_submit.Text = txt; };
        if (InvokeRequired)
        {
            BeginInvoke(m); // I need to pass txt string in some way here.
        }
        else
        {
            Invoke(m); // I need to pass txt string in some way here.
        }

person Hooch    schedule 24.04.2011    source источник
comment
@ Коди, я думаю, это сокращение от «что-то».   -  person svick    schedule 24.04.2011


Ответы (4)


Если InvokeRequired имеет значение false, вам вообще не нужно беспокоиться о вызове чего-либо — вы уже находитесь в правильном потоке.

Лучшее решение может быть примерно таким:

public delegate void InvokerDelegate(string data);
public void DoStuff(string data){
  login_submit.Text = data;
}

а затем при вызове выполните:

if (InvokeRequired){
  Invoke(InvokerDelegate(DoStuff), "something");
}
else{
  DoStuff("Something");
}

Довольно распространенный шаблон, который вы увидите, заключается в том, чтобы сделать что-то подобное для функций, которые управляют графическим интерфейсом в многопоточной среде.

public delegate void InvokerDelegate();
public void DoGuiStuff(){
  if (login_submit.InvokeRequired){
    login_submit.Invoke(InvokerDelegate(DoGuiStuff));
    return;  
  }

  login_submit.Text = "Some value";
}

Если вы используете приведенный выше шаблон, функция проверяет, требуется ли вызов, и если да, то вызывает себя в правильном потоке. Затем он возвращается. Когда он вызывает сам себя, проверка того, требуется ли вызов, возвращает false, поэтому он не беспокоится о повторном вызове самого себя - он просто запускает код.

Редактировать: я только что вернулся к winforms и попытался использовать этот шаблон только для того, чтобы потратить пару разочаровывающих минут, пытаясь понять, почему я не могу вызвать лямбду. Я подумал, что мне лучше вернуться и обновить этот ответ, чтобы добавить необходимое приведение на случай, если кто-то еще попытается его использовать.

person Russell Troywest    schedule 24.04.2011
comment
публичный недействительный делегат InvokerDelegate (строковые данные); дает мне ошибку «недопустимый токен-делегат». - person john ktejik; 23.02.2014
comment
Invoke(InvokerDelegate(DoStuff), что-то); выдает ошибку "InvokerDelegate не существует в текущем контексте" - person john ktejik; 23.02.2014
comment
Это должен быть публичный делегат void InvokerDelegate(string data); (Ключевые слова делегата и недействительными задом наперед) - person Russell Troywest; 23.02.2014

Если это обычный сценарий для вас, я предлагаю написать метод расширения:

public static class ControlExtensions
{
  public static void EnsureInvokeAsync(this Control control, Action action)
  {
     if (control.InvokeRequired) control.BeginInvoke(action);
     else action();
  }
}

class MyControl : UserControl
{
    void M(string s)
    {
       // the lambda will capture the string in a closure
       // the compiler does all the hard work for you
       this.EnsureInvokeAsync(() => _button.Text = s);
    }
}

Кроме того, вам следует изучить возможность использования BackgroundWorker или задач для асинхронных операций.

person Eli Arbel    schedule 23.02.2014
comment
Ваш ответ великолепен. Но поскольку уже принятый ответ содержит более подробную информацию, я не буду сейчас менять принятый ответ. Но я люблю это. Мне стыдно за то, что я никогда не думал об этом. - person Hooch; 23.02.2014

MethodInvoker — это тип делегата, который не иметь любые параметры. Если я вас правильно понял, вы можете сделать это так:

string txt = "some text";
MethodInvoker m = () => { login_submit.Text = txt; };
person svick    schedule 24.04.2011

Вы можете использовать замыкания для передачи значения в тело лямбды.

string value = "Login";
MethodInvoker m = () => { login_submit.Text = value; };
if (InvokeRequired)
{
    BeginInvoke(m); // I need to pass txt string in some way here.
}
else
{
    Invoke(m); // I need to pass txt string in some way here.
}

или вы можете использовать данные члена класса

person username    schedule 24.04.2011
comment
Я этого не понимаю. Как ' login_submit.Text = value; ' пройти в чем-нибудь? Разве это не просто выполняет встроенный код? - person john ktejik; 23.02.2014