C#:显式调用事件处理程序真的是“一件好事”吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/984270/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C#: is calling an event handler explicitly really "a good thing to do"?
提问by maxwellb
This question is related to C#, but may be applicable to other languages as well. I have a reservation against using code such as the following:
此问题与 C# 相关,但也可能适用于其他语言。我对使用以下代码有所保留:
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;
}
}
Am I alone, in that the above style is a pet peeve of mine? Are there others who enjoy the clarity of separating event handling from the workload of functions, or even separating out complex routines into separate functions?
难道我是一个人,因为上述风格是我的宠儿吗?是否还有其他人喜欢将事件处理与函数的工作负载分开,甚至将复杂的例程分离成单独的函数?
Is there even an accepted style? I feel like any expressiveness and flexibility that event handling in C# has can be lost with styles like this. I feel like if you have a method that means "a button has been clicked", then it should only be called when a button is clicked.
甚至有一种公认的风格吗?我觉得 C# 中的事件处理所具有的任何表现力和灵活性都可能因这样的样式而丢失。我觉得如果你有一个方法意味着“一个按钮已被点击”,那么它应该只在一个按钮被点击时被调用。
To those who write like this, I would say: if you insist on having an EventHandler method to handle your timer tick, and your button click, then call it something other than button_Click -- perhaps "handleUserEvent( object sender, EventArgs eventArgs )
".
对于像这样写的人,我会说:如果您坚持使用 EventHandler 方法来处理您的计时器滴答和您的按钮单击,那么将其称为 button_Click 以外的其他名称——也许是“ handleUserEvent( object sender, EventArgs eventArgs )
”。
Really, though, the question is, are there any style guidelines that are widely used which either support or discourage usage such as the above?
但实际上,问题是,是否有任何广泛使用的样式指南支持或阻止上述使用?
采纳答案by Daniel Paull
I agree with Rex M's answer, but I'd take it one step further. If you are using the MVC pattern (or something similar), the view would delegate the button click to the controller. The controllers methods can of course be called from elsewhere in your class - say, from your timer callback.
我同意Rex M 的回答,但我会更进一步。如果您使用的是 MVC 模式(或类似的模式),则视图会将按钮单击委托给控制器。控制器方法当然可以从您的类中的其他地方调用 - 例如,从您的计时器回调。
So, back to your original code:
所以,回到你的原始代码:
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();
}
}
One advantage of using MVC is the decoupling of the controller from the view. The controller can now be used across multiple view types easily and exiting GUIs are easier to maintain as they contain very little application logic.
使用 MVC 的优势之一是控制器与视图的分离。控制器现在可以轻松地跨多种视图类型使用,并且退出的 GUI 更易于维护,因为它们包含很少的应用程序逻辑。
EDIT: Added in response to comments from the OP
编辑:为回应 OP 的评论而添加
The fundamental design principals of software engineering talk about coupling and cohesion. Importantly we strive to minimise coupling between components while maximising cohesion as this leads to a more modular and maintainable system. Patterns like MVC and principals like the Open/Closed Principal build on these fundamentals, providing more tangible patterns of implemenation for the developer to follow.
软件工程的基本设计原则谈论耦合和内聚。重要的是,我们努力最小化组件之间的耦合,同时最大化内聚性,因为这会导致更加模块化和可维护的系统。MVC 等模式和 Open/Closed Principal 等主体建立在这些基础之上,为开发人员提供了更切实的实现模式。
So, anyone who writes code as seen in the original post has not understood the fundamentals of software design and needs to develop their skills considerably. The OP should be commended for identifying this "code smell" and trying to understand why it's not quite right.
因此,任何编写原始帖子中看到的代码的人都不了解软件设计的基础知识,需要大量提高他们的技能。OP 应该因识别这种“代码异味”并试图理解为什么它不太正确而受到赞扬。
Some relevant references:
一些相关参考资料:
- http://en.wikipedia.org/wiki/Coupling_(computer_science)
- http://en.wikipedia.org/wiki/Cohesion_(computer_science)
- http://en.wikipedia.org/wiki/Loose_coupling
- http://en.wikipedia.org/wiki/Model–view–controller
- http://en.wikipedia.org/wiki/Design_patterns
- http://en.wikipedia.org/wiki/Open/closed_principle
- http://en.wikipedia.org/wiki/Design_Patterns_(book)
- http://en.wikipedia.org/wiki/Coupling_(computer_science)
- http://en.wikipedia.org/wiki/Cohesion_(computer_science)
- http://en.wikipedia.org/wiki/Loose_coupling
- http://en.wikipedia.org/wiki/Model–view–controller
- http://en.wikipedia.org/wiki/Design_patterns
- http://en.wikipedia.org/wiki/Open/closed_principle
- http://en.wikipedia.org/wiki/Design_Patterns_(书)
回答by Franklin Munoz
The special things about events in C# (and the .Net framework in general is the delegate, which is the C/C++ equivalent of a function pointer. the method attached to the event itself is not special in any way and should be callable from anywhere.
C# 中关于事件的特殊之处(以及一般的 .Net 框架是委托,它是函数指针的 C/C++ 等价物。附加到事件本身的方法没有任何特殊之处,应该可以从任何地方调用.
Update: perhaps I should have been more verbose, but I thought my use of "should" instead of "can" or "may" would be enough. It is my assertion that event handlers should be called when the functionality they implement is needed, instead of having them become wrappers to methods that "do the work" the less method calls you have in the stack the better you will be, specially with the performance implications of .Net's exception handling.
更新:也许我应该更详细一些,但我认为我使用“应该”而不是“可以”或“可以”就足够了。我的主张是,当需要它们实现的功能时,应该调用事件处理程序,而不是让它们成为“完成工作”的方法的包装器,堆栈中的方法调用越少越好,特别是使用.Net 异常处理的性能影响。
回答by Rex M
This is definitely not a "personal preference". There is a clear, well-understood approach of how to write code that is well-structured, maintainable, reusable, and understandable. Each method in your code should encapsulate a single piece of reusable functionality. The structure of your code should be:
这绝对不是“个人喜好”。关于如何编写结构良好、可维护、可重用和可理解的代码,有一种清晰易懂的方法。代码中的每个方法都应该封装一个可重用的功能。你的代码结构应该是:
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);
}
(This is a very loose example. In a proper application everything should be separated into different classes by concern.)
回答by Blorgbeard is out
myButton.PerformClick()
is probably slightly nicer, if you don't need to pass eventargs. Sometimes you just want to simulate a click.
myButton.PerformClick()
如果您不需要传递 eventargs,可能会更好一些。有时您只想模拟点击。
But yes, I would agree that it's nicer to move the real code into another function. I prefer my event handlers to be very simple - just connect the UI to the logic, which is elsewhere.
但是,是的,我同意将真实代码移动到另一个函数中会更好。我更喜欢我的事件处理程序非常简单 - 只需将 UI 连接到其他地方的逻辑。
Then you can rearrange and redesign your UI without worrying so much about where the logic is.
然后你可以重新排列和重新设计你的 UI,而不必太担心逻辑在哪里。
回答by chris
This code increase the chance of problems if another coder works on the myButton_Click method.
如果另一个编码器在 myButton_Click 方法上工作,则此代码会增加出现问题的机会。
What if I came in to adjust the implementation of the myButton.Click handler? I might assume that the sender object is a Button, and try to cast:
如果我来调整 myButton.Click 处理程序的实现怎么办?我可能假设发件人对象是一个按钮,并尝试转换:
Button b = (Button)sender;
I have no knowledge without reading the rest of the class implementation that I'm not always receiving a Button as the sender.
如果不阅读类实现的其余部分,我就不知道我并不总是接收 Button 作为发送者。
So my point is: -1 for maintainability, because of breaking the assumptions of what objects will be passed as myButton_Click parameters.
所以我的观点是:-1 可维护性,因为打破了将作为 myButton_Click 参数传递的对象的假设。
回答by Robert Paulson
The short answer is that why would you simulate a button click by calling the handler directly? If you want to wire both methods up to the same event, you would just wire it up. Event handlers are multicast delegates, which means you can add more than one of them. Wiring up an event more than once is totally acceptable.
简短的回答是,为什么要通过直接调用处理程序来模拟按钮单击?如果您想将这两种方法连接到同一个事件,您只需连接它。事件处理程序是多播委托,这意味着您可以添加多个委托。将一个事件连接不止一次是完全可以接受的。
myTimer.Tick += myTimer_Tick;
myTimer.Tick += myButton_Click;
myButton.Click += myButton_Click;
Whether or not this is a WTF is an engineering call that we can't make from a short code snippet. However, based on your comments, it smells like a WTF. Forms or any UI should never handle business logic. They need to be business-logic-aware to some degree (as in validation) but they don't encapsulate / enforce the logic themselves.
这是否是 WTF 是一个工程调用,我们无法从短代码片段中进行调用。但是,根据您的评论,它闻起来像 WTF。表单或任何 UI 都不应该处理业务逻辑。它们需要在一定程度上了解业务逻辑(如在验证中),但它们本身并不封装/强制执行逻辑。
Going further, following some simple practices as basic refactorings and using a layered (n-tier) approach to software will take you a long way, and you will realise along the way that the code you presented smells bad.
更进一步,遵循一些简单的实践作为基本重构并使用分层(n 层)软件方法将带您走很长的路,并且您会在此过程中意识到您提供的代码闻起来很糟糕。
Eventually you'll come across some high-level patterns like MVC (model-view-controller) and MVP (model-view-presenter) which go a step beyond the simple layering. If you follow them you get a good separation of concerns.
最终,您会遇到一些高级模式,例如 MVC(模型-视图-控制器)和 MVP(模型-视图-展示器),它们超越了简单的分层。如果你遵循它们,你就可以很好地分离关注点。
I agree with the accepted answer, but jumping right into 'Use MVC', here's some code that doesn't illustrate MVC, without explaining why is a little cargo-cult for me.
我同意接受的答案,但直接进入“使用 MVC”,这里有一些代码没有说明 MVC,没有解释为什么对我来说有点货物崇拜。