С#: вызывает обработчик событий явно "хорошую вещь"?
Этот вопрос связан с С#, но может быть применим и к другим языкам. У меня есть оговорка против использования кода, например:
using System.Windows.Forms;
class MyForm : Form
{
private Timer myTimer;
private Button myButton;
public MyForm()
{
// Initialize the components, etc.
myTimer.Tick += new EventHandler( myTimer_Tick );
myButton.Click += new EventHandler( myButton_Click );
myTimer.Start();
}
private void myTimer_Tick( object sender, EventArgs eventArgs )
{
myTimer.Stop();
// also, I see a lot of usage of
// Timer.Enabled = true/false instead of -^
myButton_Click( this, ea /* or event EventArgs.Empty, or null */ );
return;
}
private void myButton_Click( object sender, EventArgs eventArgs )
{
// do a lot of stuff, with lots of logic that doesn't even use the
// state of the eventArgs
return;
}
}
Я один, в том, что вышеупомянутый стиль - домашнее животное? Есть ли другие, которые наслаждаются ясностью разделения обработки событий из рабочей нагрузки функций или даже отделяют сложные подпрограммы к отдельным функциям?
Есть ли даже принятый стиль? Я чувствую, что любая выразительность и гибкость в том, что обработка событий на С# может быть потеряна в таких стилях. Я чувствую, что если у вас есть метод, который означает "нажата кнопка", тогда его следует вызывать только при нажатии кнопки.
Для тех, кто пишет так, я бы сказал: если вы настаиваете на том, чтобы метод EventHandler обрабатывал ваш таймер и нажмите кнопку, тогда назовите его чем-то иным, чем button_Click - возможно, "handleUserEvent( object sender, EventArgs eventArgs )
".
Действительно, вопрос в том, есть ли какие-либо широко используемые стили, которые либо поддерживают, либо препятствуют использованию, например, выше?
Ответы
Ответ 1
Я согласен с Rex M answer, но я бы сделал это еще на один шаг. Если вы используете шаблон MVC (или что-то подобное), представление должно делегировать кнопку щелчка на контроллер. Разумеется, методы контроллеров могут быть вызваны из другого места вашего класса - скажем, из вашего обратного вызова по таймеру.
Итак, вернемся к исходному коду:
using System.Windows.Forms;
class MyForm : Form
{
private Timer myTimer;
private Button myButton;
private MyController myController;
public MyForm()
{
// ...
// Initialize the components, etc.
// ...
myTimer.Tick += new EventHandler( myTimer_Tick );
myButton.Click += new EventHandler( myButton_Click );
myTimer.Start();
}
private void myTimer_Tick( object sender, EventArgs eventArgs )
{
myTimer.Stop();
myController.SomeMethod()
}
private void myButton_Click( object sender, EventArgs eventArgs )
{
// All the stuff done here will likely be moved
// into MyController.SomeMethod()
myController.SomeMethod();
}
}
Одним из преимуществ использования MVC является развязка контроллера с представлением. Теперь контроллер можно легко использовать в нескольких типах представлений, и выходить из графических интерфейсов проще, поскольку они содержат очень небольшую логику приложений.
EDIT: добавлено в ответ на комментарии OP
Основные принципы проектирования программного обеспечения говорят о связи и сцеплении. Важно отметить, что мы стремимся минимизировать сцепление между компонентами при максимальном сцеплении, поскольку это приводит к более модульной и поддерживаемой системе. Такие шаблоны, как MVC и руководители, такие как Open/Closed Principal, основываются на этих фундаментальных принципах, обеспечивая более ощутимые шаблоны реализации для разработчиков.
Таким образом, любой, кто пишет код, как видно в оригинальном посте, не понял основ разработки программного обеспечения и должен значительно развивать свои навыки. ОП следует поблагодарить за выявление этого "запаха кода" и попытку понять, почему это не совсем правильно.
Некоторые релевантные ссылки:
Ответ 2
Это определенно не "личное предпочтение". Существует четкий, понятный подход, как написать хорошо структурированный, поддерживаемый, многоразовый и понятный код. Каждый метод в вашем коде должен инкапсулировать один элемент многоразовой функциональности. Структура вашего кода должна быть:
void ButtonClickEventHandler(...)
{
UserData userData = //determine user data from event data
DoUserThing(userData);
}
void DoUserThing(UserData userData)
{
//do stuff
}
void SomeOtherMethod()
{
UserData userData = //get userdata from some other source
DoUserThing(userData);
}
(Это очень непривлекательный пример. В правильном приложении все должно быть разделено на разные классы по соображениям.)
Ответ 3
myButton.PerformClick()
, вероятно, немного приятнее, если вам не нужно передавать eventargs. Иногда вы просто хотите имитировать щелчок.
Но да, я бы согласился, что лучше переместить настоящий код в другую функцию. Я предпочитаю, чтобы мои обработчики событий были очень простыми - просто подключите интерфейс к логике, которая находится в другом месте.
Затем вы можете изменить и изменить свой пользовательский интерфейс, не беспокоясь о том, где находится логика.
Ответ 4
Этот код увеличивает вероятность возникновения проблем, если другой кодер работает с методом myButton_Click.
Что делать, если я пришел, чтобы настроить реализацию myButton.Click-обработчика? Я мог бы предположить, что объект-отправитель является Button, и попробуйте сделать:
Button b = (Button)sender;
У меня нет знаний, не прочитав остальную часть реализации класса, что я не всегда получаю кнопку в качестве отправителя.
Итак, моя точка зрения: -1 для удобства обслуживания, из-за нарушения предположений о том, какие объекты будут переданы как параметры myButton_Click.
Ответ 5
Короткий ответ заключается в том, почему вы хотели бы имитировать нажатие кнопки, вызвав непосредственно обработчик? Если вы хотите подключить оба метода до одного и того же события, вы просто подключите его. Обработчики событий - это многоадресные делегаты, что означает, что вы можете добавить более одного из них. Проводка события более одного раза полностью приемлема.
myTimer.Tick += myTimer_Tick;
myTimer.Tick += myButton_Click;
myButton.Click += myButton_Click;
Независимо от того, является ли это WTF, это технический вызов, который мы не можем сделать из короткого фрагмента кода. Однако, основываясь на ваших комментариях, он пахнет WTF. Формы или любой пользовательский интерфейс никогда не должны обрабатывать бизнес-логику. Они должны быть в некоторой степени осведомлены о бизнес-логике (как в проверке), но они не инкапсулируют/не обеспечивают реализацию самой логики.
Идем дальше, следуя некоторым простым практикам в качестве базовых рефакторингов и используя многоуровневый подход к программному обеспечению, вы пройдете долгий путь, и вы поймете, что код, который вы представили, плохо пахнет.
В конце концов вы столкнетесь с некоторыми шаблонами высокого уровня, такими как MVC (model-view-controller) и MVP (model-view-presenter), которые выходят за рамки простого расслоения. Если вы будете следовать им, вы получите хорошее разделение проблем.
Я согласен с принятым ответом, но прыгаю прямо в "Использовать MVC", вот какой-то код, который не иллюстрирует MVC, не объясняя, почему для меня маленький грузовой кук.
Ответ 6
Особые вещи о событиях на С# (и в .NET-инфраструктуре вообще - это делегат, который является эквивалентом C/С++ указателя на функцию. Метод, связанный с самим событием, не является каким-либо особым и должен быть вызываемый из любого места.
Обновление:
возможно, мне следовало бы быть более подробным, но я думал, что моего использования "должно" вместо "может" или "возможно" будет достаточно. Мое утверждение о том, что обработчики событий следует вызывать, когда нужны функциональные возможности, вместо того, чтобы заставить их стать оболочками методов, которые "выполняют работу", тем меньше вызовов метода, которые у вас есть в стеке, тем лучше, чем вы это сделаете, особенно с последствия производительности .NET обработки исключений.