Синтаксис Dispatcher BeginInvoke

Я пытался следовать некоторым примерам служб данных WCF и получил следующий код:

private void OnSaveCompleted(IAsyncResult result)
    {
        Dispatcher.BeginInvoke(() =>
        {
            context.EndSaveChanges(result);
        });
    }

Что вызывается следующим:

this.context.BeginSaveChanges(SaveChangesOptions.Batch, this.OnSaveCompleted, null);

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

Лямбда-выражение типа аргумента не может быть присвоено типу параметра System.Delegate

Поэтому вместо того, чтобы слепо следовать образцу кода, я попытался понять, что здесь происходит. К сожалению, я изо всех сил пытаюсь понять ошибку и то, что на самом деле происходит. Кто-нибудь может объяснить?

Я чувствую себя немного глупо, потому что уверен, что это легко.


person Jon Archway    schedule 21.09.2010    source источник


Ответы (3)


Проблема в том, что компилятор не знает, в какой делегат вы пытаетесь преобразовать лямбда-выражение. Вы можете исправить это либо с помощью приведения, либо отдельной переменной:

private void OnSaveCompleted(IAsyncResult result)
{        
    Dispatcher.BeginInvoke((Action) (() =>
    {
        context.EndSaveChanges(result);
    }));
}

or

private void OnSaveCompleted(IAsyncResult result)
{
    Action action = () =>
    {
        context.EndSaveChanges(result);
    };
    Dispatcher.BeginInvoke(action);
}
person Jon Skeet    schedule 21.09.2010
comment
Спасибо, но теперь я получаю Невозможно получить доступ к нестатическому методу BeginInvoke в статическом контексте. Я запутался больше, так как это не статический метод? - person Jon Archway; 21.09.2010
comment
@Jon: он думает, что вы пытаетесь использовать BeginInvoke как статический метод в классе Dispatcher, тогда как вы хотите использовать Dispatcher свойство, а затем вызвать BeginInvoke в соответствующем экземпляре. Я предполагаю, что это не соответствующий класс со свойством Dispatcher. Только что увидев, что это WCF, я не уверен, откуда у вас Dispatcher. Я больше привык использовать его из WPF и Silverlight. - person Jon Skeet; 21.09.2010
comment
На самом деле это класс ViewModel в приложении WPF - person Jon Archway; 21.09.2010
comment
ViewModel обычно не знает представления, не говоря уже о связанном диспетчере. Вы можете использовать Dispatcher.CurrentDispatcher, но я настоятельно не рекомендую этого делать (вы легко можете оказаться в неправильном потоке, и делегат никогда не будет вызван); На мой взгляд, лучший способ - использовать что-то вроде MVVMLight Messenger и отправить сообщение представлению - сообщение может содержать действие, а представление может вызывать его с помощью своего диспетчера. - person Alex Paven; 21.09.2010
comment
@Jon: определенно стоит обновить вопрос, чтобы отразить это. Часть WCF не имеет значения, но часть WPF очень актуальна. Что касается получения диспетчера в ViewModel - вы можете использовать мессенджер, как предложил Алекс, или вы можете внедрить Dispatcher в ViewModel ... не как диспетчер, а с точки зрения оболочки вокруг него, реализуя свой собственный интерфейс. Таким образом, вы можете проверить, что вы поступаете правильно с точки зрения безопасности потоков. Я делал это раньше, и это хорошо сработало. - person Jon Skeet; 21.09.2010
comment
@Jon (Skeet) - Интересная идея; однако я смутно помню, что у меня возникли проблемы с чем-то похожим, потому что в тестовой среде, такой как MSTest (консольное приложение), Dispatcher не запускается автоматически, и вам нужно перепрыгнуть через некоторые обручи, чтобы ваши делегаты вызывались правильно. Однако я могу спутать это с чем-то другим ... больше не уверен, уже поздно, и я устал. - person Alex Paven; 21.09.2010
comment
@ мой предыдущий комментарий: На самом деле неважно, конечно, если вы вводите диспетчер с помощью интерфейса, вы можете издеваться над ним во время тестирования. - person Alex Paven; 21.09.2010
comment
Какое было решение? У меня та же проблема, что и у меня: ссылка на объект требуется для нестатического поля, метода или свойства 'System.Windows.Threading.Dispatcher.BeginInvoke (System.Delegate, params object [])'. Невозможно использовать внешний фреймворк или пакет NuGet. - person Kala J; 01.08.2014
comment
@KalaJ: Ну, по-видимому, вы находитесь в статическом методе ... вы должны вызвать его в конкретном диспетчере. Ваша проблема полностью отличается от проблемы в этом вопросе, где Dispatcher означает Dispatcher свойство в элементе управления. - person Jon Skeet; 01.08.2014
comment
@JonSkeet, Нет, мой метод не статичен. Решение, которое я нашел, заключалось в том, что мне нужно было получить диспетчер из потока пользовательского интерфейса, а затем передать его моей задаче. - person Kala J; 01.08.2014
comment
@Kala: Ну, вы должны получить его из компонента пользовательского интерфейса ... Не из потока. - person Jon Skeet; 01.08.2014
comment
@JonSkeet, какая разница? Я думал об этом так, что у меня есть поток пользовательского интерфейса и поток фона. Мне пришлось получить его из потока пользовательского интерфейса и передать его фоновому потоку. - person Kala J; 01.08.2014
comment
@KalaJ: В мире есть большая разница между компонентом и потоком. Но пока у вас есть решение, это, вероятно, нормально. Если хотите узнать больше, задайте новый вопрос. - person Jon Skeet; 01.08.2014
comment
упс, только что возникла та же проблема (с упомянутым выше нестатическим) при получении CS0120 Ссылка на объект требуется для нестатического поля, метода или свойства при выполнении общедоступной void Speak (текст строки) {Dispatcher.BeginInvoke (new Действие (() = ›SpeakSynthesizer.Speak (текст))); } - ВЫКЛЮЧАЕТСЯ Мне нужно было сделать общедоступную void Speak (текст строки) {Dispatcher.CurrentDispatcher.BeginInvoke (new Action (() = ›SpeakSynthesizer.Speak (text))); }. Я думаю, что Visual Studio 2015 должна добавить дополнительную лампочку для этого, немного сложно уловить - person George Birbilis; 06.09.2015
comment
Кажется, люди запутались и используют Dispatcher.BeginInvoke вместо Dispatcher.CurrentDispatcher.BeginInvoke. Это связано с тем, что в WPF доступно свойство Dispatcher, поэтому они пытаются повторно использовать шаблон кода, который они помнят, в коде за пределами окна WPF. - person George Birbilis; 06.09.2015

Ответ Джона Скита очень хорош, но есть и другие возможности. Я предпочитаю "начать вызывать новое действие", которое мне легко читать и запоминать.

private void OnSaveCompleted(IAsyncResult result)
{       
    Dispatcher.BeginInvoke(new Action(() =>
    {
        context.EndSaveChanges(result);
    }));
}

or

private void OnSaveCompleted(IAsyncResult result)
{       
    Dispatcher.BeginInvoke(new Action(delegate
    {
        context.EndSaveChanges(result);
    }));
}

or

private void OnSaveCompleted(IAsyncResult result)
{       
    Dispatcher.BeginInvoke(new Action(() => context.EndSaveChanges(result)));
}
person CoperNick    schedule 14.09.2012
comment
может быть, выполнение нового действия обходится дороже, чем просто выполнение (действия)? - person George Birbilis; 06.09.2015

Если ваш метод не требует параметров, это самая короткая версия, которую я нашел

Application.Current.Dispatcher.BeginInvoke((Action)MethodName); 
person Alon Amsalem    schedule 25.04.2017