MethodInvoker против действия для Control.BeginInvoke
Что правильнее и почему?
Control.BeginInvoke(new Action(DoSomething), null);
private void DoSomething()
{
MessageBox.Show("What a great post");
}
или же
Control.BeginInvoke((MethodInvoker) delegate {
MessageBox.Show("What a great post");
});
Я чувствую, что делаю то же самое, поэтому, когда подходящее время MethodInvoker
против Action
или даже писать лямбда-выражение?
РЕДАКТИРОВАТЬ: я знаю, что на самом деле нет большой разницы между написанием лямбда против Action
, но MethodInvoker
кажется, сделано для определенной цели. Это делает что-то другое?
7 ответов
Оба одинаково верны, но документация для Control.Invoke
говорится, что:
Делегат может быть экземпляром EventHandler, и в этом случае параметр отправителя будет содержать этот элемент управления, а параметр события будет содержать EventArgs.Empty. Делегат также может быть экземпляром MethodInvoker или любым другим делегатом, который принимает список параметров void. Вызов делегата EventHandler или MethodInvoker будет быстрее, чем вызов делегата другого типа.
Так MethodInvoker
будет более эффективным выбором.
Для каждого решения ниже я запускаю 131072 (128*1024) итерации (в одном отдельном потоке). Помощник по производительности VS2010 дает следующие результаты:
- MethodInvoker только для чтения: 5664,53 (+0%)
- Новый MethodInvoker: 5828,31 (+2,89%)
- Функция приведена в MethodInvoker: 5857,07 (+3,40%)
- Действие только для чтения: 6467,33 (+14,17%)
- Новая акция: 6829,07 (+20,56%)
Призыв к новому действию на каждой итерации
private void SetVisibleByNewAction()
{
if (InvokeRequired)
{
Invoke(new Action(SetVisibleByNewAction));
}
else
{
Visible = true;
}
}
Вызов только для чтения, встроенный конструктор, действие на каждой итерации
// private readonly Action _actionSetVisibleByAction
// _actionSetVisibleByAction= SetVisibleByAction;
private void SetVisibleByAction()
{
if (InvokeRequired)
{
Invoke(_actionSetVisibleByAction);
}
else
{
Visible = true;
}
}
Вызывайте новый MethodInvoker на каждой итерации.
private void SetVisibleByNewMethodInvoker()
{
if (InvokeRequired)
{
Invoke(new MethodInvoker(SetVisibleByNewMethodInvoker));
}
else
{
Visible = true;
}
}
Вызов только для чтения, встроенный конструктор, MethodInvoker на каждой итерации
// private readonly MethodInvoker _methodInvokerSetVisibleByMethodInvoker
// _methodInvokerSetVisibleByMethodInvoker = SetVisibleByMethodInvoker;
private void SetVisibleByMethodInvoker()
{
if (InvokeRequired)
{
Invoke(_methodInvokerSetVisibleByMethodInvoker);
}
else
{
Visible = true;
}
}
Вызов функции, приведенной в MethodInvoker на каждой итерации
private void SetVisibleByDelegate()
{
if (InvokeRequired)
{
Invoke((MethodInvoker) SetVisibleByDelegate);
}
else
{
Visible = true;
}
}
Пример вызова решения "Новое действие":
private void ButtonNewActionOnClick(object sender, EventArgs e)
{
new Thread(TestNewAction).Start();
}
private void TestNewAction()
{
var watch = Stopwatch.StartNew();
for (var i = 0; i < COUNT; i++)
{
SetVisibleByNewAction();
}
watch.Stop();
Append("New Action: " + watch.ElapsedMilliseconds + "ms");
}
Я предпочитаю использовать лямбды и Actions/Funcs:
Control.BeginInvoke(new Action(() => MessageBox.Show("What a great post")));
Действие определено в System, а MethodInvoker определено в System.Windows.Forms - вам лучше использовать Action, поскольку он переносим в другие места. Вы также найдете больше мест, которые принимают Action в качестве параметра, чем MethodInvoker.
Однако в документации указано, что вызовы делегатов типа EventHandler или MethodInvoker в Control.Invoke() будут выполняться быстрее, чем любой другой тип.
Независимо от того, в каком пространстве имён они находятся, я не верю, что между Action и MethodInvoker есть существенное функциональное различие - они, по сути, оба определены как:
public delegate void NoParamMethod();
Кроме того, Action имеет несколько перегрузок, которые позволяют передавать параметры - и это универсально, так что они могут быть безопасными для типов.
Также в MSDN:
MethodInvoker предоставляет простой делегат, который используется для вызова метода со списком параметров void. Этот делегат может использоваться при вызове метода Invoke элемента управления или когда вам нужен простой делегат, но вы не хотите определять его самостоятельно.
Действие с другой стороны может принимать до 4 параметров.
Но я не думаю, что есть разница между MethodInvoker и Action, так как они просто инкапсулируют делегата, который не берет паромер и возвращает void
Если вы посмотрите на их определения, вы просто увидите это.
public delegate void MethodInvoker();
public delegate void Action();
Кстати, вы также можете написать вторую строку как.
Control.BeginInvoke(new MethodInvoker(DoSomething), null);
В большинстве случаев это вопрос предпочтения, если только вы не собираетесь использовать метод DoSomething(). Кроме того, анонимные функции поместят переменные в области видимости в кучу, что может сделать эту функцию более дорогой.
Не забудьте как-то проверить, доступен ли контроль в данный момент, чтобы избежать ошибок при закрытии формы.
if(control.IsHandleCreated)
control.BeginInvoke((MethodInvoker)(() => control.Text="check123"));