C#中的虚方法或事件

Dar*_*rio 9 c# design-patterns

我目前正在编写一个小C#库来简化实现很少的物理模拟/实验.

主要组件是在SimulationForm内部运行定时器循环并从用户隐藏样板代码.实验本身将通过三种方法定义:

  1. Init() (初始化一切)
  2. Render(Graphics g) (渲染当前模拟状态)
  3. Move(double dt)(移动实验dt几秒钟)

我只是想知道让用户实现这些功能的更好选择是什么:

1)由继承表单覆盖的虚拟方法

protected virtual void Init() {} 
...
Run Code Online (Sandbox Code Playgroud)

要么

2)事件

public event EventHandler<MoveSimulationEventArgs> Move = ...
...
Run Code Online (Sandbox Code Playgroud)

编辑:请注意,方法不应该是抽象的.其实,还有更和他们都不具有实施.由于许多模拟不需要它们,因此将它们放在外面通常很方便.

关于这是一个"正常形式"的酷事是你可以写

partial class frmMyExperiment : SimulationForm {
}
Run Code Online (Sandbox Code Playgroud)

并且您完全能够与设计器和所有继承的控件和设置/属性进行交互.我不想通过采用完全不同的方法来失去这些功能.

Ree*_*sey 17

在这种情况下,我更喜欢使用虚方法.

此外,如果需要方法才能运行,则可以将类设为抽象类.这在可用性方面是最好的,因为它会导致编译器强制使用.如果用户尝试使用您的类/表而不实现方法,编译器将会抱怨.

事件在这里有点不合适,因为这不是你想要多于一个实现(事件允许多个订阅者)的东西,并且从概念上讲,它更像是对象的功能,而不是由物体.


LBu*_*kin 15

有趣的是,我不得不在最近工作的设计中做出类似的选择.一种方法并不严格优于另一种方法.

在你的例子中,没有额外的信息,我可能会选择虚拟方法 - 特别是因为我的直觉让我相信你会使用继承来模拟不同类型的实验,而你不需要拥有多个订阅者的能力(作为事件)允许).

以下是关于在这些模式之间进行选择的一些一般性观察:

活动很棒:

  1. 当您不需要调用者返回任何信息时,以及何时需要可扩展性而不需要子类化.
  2. 如果您希望允许呼叫者拥有多个可以侦听和响应事件的订阅者.
  3. 因为它们自然地将自己安排到模板方法模式中 - 这有助于避免引入脆弱的基类问题.

事件的最大问题是管理订阅者的生命周期可能会变得棘手,并且当订阅者订阅的时间超过必要时间时,您可能会引入泄漏甚至功能缺陷.第二个最大的问题是允许多个订阅者可以创建一个令人困惑的实现,其中各个订阅者互相踩踏 - 或者表现出顺序依赖性.

虚拟方法运行良好:

  1. 当你只希望继承者能够改变一个类的行为时.
  2. 当你需要从通话中返回信息时(哪个事件不容易支持)
  3. 当您只需要一个订阅者到特定扩展点时
  4. 当你想要衍生物的衍生物能够覆盖行为时.

虚方法的最大问题是您可以轻松地将脆弱的基类问题引入到您的实现中.虚方法本质上是与派生类的契约,您必须清楚地记录它们,以便继承者可以提供有意义的实现.

虚方法的第二大问题是它可以引入深度或广泛的继承树,以便定制在特定情况下如何自定义类的行为.这可能没问题,但如果is-a问题域中没有明确的关系,我通常会尝试避免继承.

您可以考虑使用另一种解决方案:策略模式.让您的类支持对象(或委托)的分配,以定义Render,Init,Move等应该如何表现.您的基类可以提供默认实现,但允许外部使用者更改行为.虽然与事件类似,但优点是您可以将值返回给调用代码,并且您只能强制执行一个订阅者.


Mat*_*ias 5

决定一点帮助:

您想通知其他(多个)对象吗?然后使用事件.

你想让不同的实现成为可能/使用多态吗?然后使用虚拟/抽象方法.

在某些情况下,即使是两种方式组合也是一个很好的解决方案.