Анонимный метод в вызове Invoke
-
05-07-2019 - |
Вопрос
Возникли небольшие проблемы с синтаксисом, когда мы хотим анонимно вызвать делегата внутри элемента управления.Invoke.
Мы испробовали несколько различных подходов, но все безрезультатно.
Например:
myControl.Invoke(delegate() { MyMethod(this, new MyEventArgs(someParameter)); });
где некоторый параметр является локальным для этого метода
Вышесказанное приведет к ошибке компилятора:
Не удается преобразовать анонимный метод в тип 'System.Делегировать', поскольку это не тип делегата
Решение
Потому что Invoke
/BeginInvoke
принимает Delegate
(вместо типизированного делегата) вам нужно указать компилятору, какой тип делегата создавать ; MethodInvoker
(2.0) или Action
(3.5) являются распространенными вариантами (обратите внимание, что они имеют одинаковую подпись).;вот так:
control.Invoke((MethodInvoker) delegate {this.Text = "Hi";});
Если вам нужно передать параметры, то "захваченные переменные" - это способ:
string message = "Hi";
control.Invoke((MethodInvoker) delegate {this.Text = message;});
(предостережение:вам нужно быть немного осторожным при использовании захватов асинхронный, но синхронизация все в порядке - т. е.все вышесказанное прекрасно)
Другой вариант - написать метод расширения:
public static void Invoke(this Control control, Action action)
{
control.Invoke((Delegate)action);
}
тогда:
this.Invoke(delegate { this.Text = "hi"; });
// or since we are using C# 3.0
this.Invoke(() => { this.Text = "hi"; });
Конечно, вы можете сделать то же самое с BeginInvoke
:
public static void BeginInvoke(this Control control, Action action)
{
control.BeginInvoke((Delegate)action);
}
Если вы не можете использовать C # 3.0, вы могли бы сделать то же самое с обычным методом экземпляра, предположительно в Form
базовый класс.
Другие советы
На самом деле вам не нужно использовать ключевое слово делегата. Просто передайте лямбду в качестве параметра:
control.Invoke((MethodInvoker)(() => {this.Text = "Hi"; }));
myControl.Invoke(new MethodInvoker(delegate() {...}))
Вам необходимо создать тип делегата. Ключевое слово «делегат» при создании анонимного метода немного вводит в заблуждение. Вы создаете не анонимный делегат, а анонимный метод. Созданный вами метод можно использовать в делегате. Вот так:
myControl.Invoke(new MethodInvoker(delegate() { (MyMethod(this, new MyEventArgs(someParameter)); }));
Для полноты картины это также можно выполнить с помощью комбинации метода Action / анонимного метода:
//Process is a method, invoked as a method group
Dispatcher.Current.BeginInvoke((Action) Process);
//or use an anonymous method
Dispatcher.Current.BeginInvoke((Action)delegate => {
SomeFunc();
SomeOtherFunc();
});
У меня были проблемы с другими предложениями, потому что я хочу иногда возвращать значения из моих методов. Если вы попытаетесь использовать MethodInvoker с возвращаемыми значениями, вам это не понравится. Таким образом, решение, которое я использую, выглядит следующим образом (очень рад услышать способ сделать это более кратким - я использую c # .net 2.0):
// Create delegates for the different return types needed.
private delegate void VoidDelegate();
private delegate Boolean ReturnBooleanDelegate();
private delegate Hashtable ReturnHashtableDelegate();
// Now use the delegates and the delegate() keyword to create
// an anonymous method as required
// Here a case where there's no value returned:
public void SetTitle(string title)
{
myWindow.Invoke(new VoidDelegate(delegate()
{
myWindow.Text = title;
}));
}
// Here's an example of a value being returned
public Hashtable CurrentlyLoadedDocs()
{
return (Hashtable)myWindow.Invoke(new ReturnHashtableDelegate(delegate()
{
return myWindow.CurrentlyLoadedDocs;
}));
}
Я никогда не понимал, почему это имеет значение для компилятора, но этого достаточно.
public static class ControlExtensions
{
public static void Invoke(this Control control, Action action)
{
control.Invoke(action);
}
}
Бонус: добавьте некоторую обработку ошибок, потому что, вероятно, если вы используете Control.Invoke
из фонового потока, вы обновляете состояние text / progress / enabled элемента управления и не делаете не волнует, если элемент управления уже расположен.
public static class ControlExtensions
{
public static void Invoke(this Control control, Action action)
{
try
{
if (!control.IsDisposed) control.Invoke(action);
}
catch (ObjectDisposedException) { }
}
}