何时使用回调代替c#中的事件?

Ben*_*jol 35 c# events

请原谅我这是一个愚蠢的问题,我承认我没有多想过.

但是,您何时愿意使用回调(即传入Func或Action),而不是暴露和使用事件?

UPDATE

这个问题的动机是以下问题:

我有一个ThingsHandler类,可以与ThingEditor关联.ThingsHandler处理事物列表,知道它们的顺序,哪一个是"当前的",添加或删除新的等等.

ThingEditors只能修改一件事.

当用户选择要编辑的新Thing时,ThingsHandler需要提醒ThingEditor,当用户说"完成"时,ThingEditor需要提醒ThingsHandler.

困扰我的是让这两个类彼此引用 - 虽然我猜这是不可避免的 - 或者绑定到两个方向的事件.我想知道在一个方向上使用回调是否"更清洁".

我怀疑这有一种设计模式,并为我的无知(和懒惰)谦卑地道歉.

Eri*_*ert 28

虽然到目前为止其他答案看似合理,但我会采取更具哲学性的方法.

类是一个机制,模型有一种特殊的东西在一个特定的领域.编写类的内部细节以将机制实现细节正在建模语义混淆时非常容易.我的意思的一个简短例子:

class Giraffe : Mammal, IDisposable
{
    public override void Eat(Food f) { ... }
    public void Dispose() { ... }
}
Run Code Online (Sandbox Code Playgroud)

请注意我们如何将现实世界的物体(长颈鹿是一种哺乳动物,长颈鹿吃食物)与实施细节混为一谈(长颈鹿的一个实例是一个可以用"使用"处理的物体. "声明".我保证,如果你去动物园,你将永远不会看到长颈鹿被处理使用声明.我们把这里的水平搞混了,这是不幸的.

我尝试使用事件(和属性)作为语义模型的一部分,并使用回调方法(和字段)作为机制的一部分.我会让GaveBirth成为长颈鹿的事件,因为这是我们试图捕捉的真实世界长颈鹿行为模型的一部分.如果我有一些机制,比如说,我想实现一个顺序遍历树遍历算法,它走遍了长颈鹿的家谱,并在每一个上调用一个方法,那么我会说这显然是一个机制,而不是一部分该模型,并使其成为一个回调,而不是试图将其塞进事件模型中.

  • 很好的答案,并总结:事件是通知(生育),回调是请求(吃食物).将它与现实世界联系起来,你会得到一个用长颈鹿作为对象的用例图,以及用户作为动物处理程序(或者用爆米花把它扔进酒吧的小孩) (4认同)
  • Darn,你让我的大脑再次受伤:)在我目前的代码中,这些类远离现实世界的任何东西,我不知道我从哪里开始...(见更新的问题) (2认同)

Mar*_*ell 27

我使用回调在少数情况下,我知道,它只会不断开火一次,回调是具体到一个方法调用(而不是一个对象实例) -例如,作为异步方法的返回部分.

对于静态实用程序方法尤其如此(因为您没有实例,并且在不小心使用并且要避免时静态事件是致命的),但当然另一种选择是使用事件创建类实例.


Cod*_*uth 23

通常,如果需要,我使用回调,而当它应该是可选的时使用事件.如果您期望总是听某事,请不要公开活动.

考虑以下:

public class MyClass_Event
{
    public event EventHandler MakeMeDoWork;

    public void DoWork()
    {
        if (MakeMeDoWork == null)
            throw new Exception("Set the event MakeMeDoWork before calling this method.");
        MakeMeDoWork(this, EventArgs.Empty);
    }
}
Run Code Online (Sandbox Code Playgroud)

与:

public class MyClass_Callback
{
    public void DoWork(EventHandler callback)
    {
        if (callback == null)
            throw new ArgumentException("Set the callback.", "callback"); // better design
        callback(this, EventArgs.Empty);
    }
}
Run Code Online (Sandbox Code Playgroud)

代码几乎与回调可以作为null传递相同,但至少抛出的异常可能更相关.


Jas*_*ams 9

当一个对象希望接收单个通知时(例如,运行异步数据读取然后使用结果调用您),回调就很好.

事件适用于可由任意数量的侦听器接收的重复通知.


Pao*_*olo 5

就面向对象设计和类耦合而言,回调接口和事件之间没有太大区别。

然而,我更喜欢类需要向有兴趣监听的人“大喊大叫”的事件(通常是多个事件)以及特定类请求异步操作的回调。

无论您使用什么,请在整个代码库中一致地使用它们!


eri*_*len 5

一个例子是回调何时应返回某些内容。例如(愚蠢的例子):

public int Sum(Func<int> callbackA, Func<int> callbackB) {
    return callbackA() + callbackB();
}

public void UseSum() {
    return sum(() => 10, () => 20);
}
Run Code Online (Sandbox Code Playgroud)