C#:理解事件语法

6

我需要帮助理解如何创建新的自定义事件。我从这里了解到...

public delegate void ChangingHandler (object sender, CarArgs ca); 
public event ChangingHandler Change; 
...
private void car_Change(object sender, CarArgs ca) {
    MessageBox.Show(ca.Message());
} 
...
car.Change+=new Car.ChangingHandler(car_Change); // add event handler
...
Change(this,ca); // call event

首先,我不太理解委托部分。在普通变量声明中,

protected string str1;

但是这里我有额外的(ChangingHandler)。我该如何理解它?我知道它类似于用于处理事件的 ChangingHandler,但它让我有点困惑。

public event ChangingHandler Change

那么

car.Change+=new Car.ChangingHandler(car_Change)

我不太明白语法 new Car.ChangingHandler(car_Change) 的含义。
5个回答

17
在C#中,事件类似于方法指针的集合。它会说:“嘿,大家如果关心我,请给我一个可以调用的方法指针,我会保存它,当我想向世界宣布消息时,我会调用你们给我的所有方法。”
这样,某人可以给事件提供一个指向他们的方法的指针,这被称为“事件处理程序”。每当事件所有者觉得适当时,事件将调用此方法。
在这种情况下,委托只是说明事件将接受什么类型的方法。您不能让一个人给事件提供一个不带参数的方法,而另一个人提供一个带有5个参数的方法,否则它无法调用它们。因此,委托是事件和事件处理程序之间的契约,告诉它们两者对于方法签名应该期望什么。
在你的情况下,最好只使用EventHandler<T>,它是一个内置的委托,形式为void EventHandler<T>(object sender, T eventArgs),用于你的事件委托,就像这样:
public event EventHandler<CarArgs> Change;

C#实际上没有原始意义上的函数指针。委托来代替处理这个问题。它们就像强类型的、面向对象的函数指针。当你调用

car.Change+=new Car.ChangingHandler(car_Change);

您正在为事件指定一个新的委托(函数指针),该委托指向您的car_Change事件处理程序,告诉事件在准备就绪时调用您的car_Change方法。委托(new ChangeHandler(...))简单地包装了指向car_Change方法的指针。


1
注意,您不需要显式创建委托实例。您可以执行 car.Change += car_Change;(这相当惯用)。 - bruceboughton

11

一个事件具有特定的签名,该签名定义了监听器应该具备哪些参数和返回类型。这个契约通过定义委托来表达。从你的代码示例中:

public delegate void ChangingHandler (object sender, CarArgs ca);

这里我们定义了一个委托,用于接受一个object和一个CarArgs作为参数,并具有void返回类型的方法。

接下来我们声明事件如下:

public event ChangingHandler Change;

所以,我们有一个名为Change的事件,事件的监听器必须与ChangingHandler委托具有相同的签名。

然后,我们需要一个监听器方法:

private void car_Change(object sender, CarArgs ca) {
    MessageBox.Show(ca.Message());
} 

我们可以看到它和ChangingHandler委托具有相同的签名。

最后,我们可以将监听器附加到事件上:

car.Change+=new Car.ChangingHandler(car_Change)

因此,我们创建一个新的ChangingHandler实例,它引用car_Change方法,并将委托实例传递给事件。

尽管如此,我建议使用预定义的EventHandler<T>委托,而不是创建自己的委托:

public event EventHandler<CarArgs> Change;

3
此外,事实证明你不需要使用car.Change += new Car.ChangingHandler( car_Change );这个语法,正如你所指出的那样,它并不是很直观。
假设car_Change具有正确的方法签名,你可以简单地写成:car.Change += car_Change;

1

事件基于委托概念,委托基本上是方法签名的定义。就像在接口中定义方法签名一样,但您不需要在接口中实现它们,而是在所有继承该接口的类中实现。

委托是一个方法签名的定义,可以为其定义任意数量的方法体,例如,给定此委托(方法签名定义):

public delegate void ChangingHandler (object sender, CarArgs ca);

您可以使用此委托(方法签名定义)来定义函数体,如下所示:

public void SomeMethodWhichCreatesADelegateBody()
{
    ChangingHandler myChangingHandler = new ChangingHandler(
        delegate(object sender, EventArgs e) { /* the method body for myChangingHandler */ }
    );
}

这是一种旧的定义委托的方式,现在更推荐使用lambda表达式来创建方法体,使代码更易读。但这并不影响问题的解决。

现在可以将事件想象成一个带有一系列方法体的委托(方法签名定义),这些方法体被称为订阅事件,订阅方法体到事件的语法是+=,同时也有从事件订阅中移除方法体的语法-=

因此,这段代码定义了一个ChangingHandler委托(方法签名定义)的方法体数组:

public event ChangingHandler Change;

你可以通过调用一个方法将body(将它们添加到数组中)订阅到这个分组中:

public void SomeMethodWhichSubscribesADelegateBodyToAnEvent()
{
    ChangingHandler myChangingHandler = new ChangingHandler(
        delegate(object sender, EventArgs e) { /* the method body for myChangingHandler */ }
    );

    Change += myChangingHandler;
}

现在事件具有方法体数组的整个原因是,您可以定义和添加任意数量的方法体,以便每当该对象内部发生事件时,该对象可以执行所有这些方法来完成任何您想要完成的操作。拥有该事件的对象可以像这样执行所有这些方法:
if (Change != null) // You wouldn't access an array without making sure it wasn't null, would you?
{
    Change(this, new CarArgs()); // This executes every method body in it's array using the signature definition the delegate defined.
    // The delegate simply exists so this code knows the method signature
    // so it can know how to call those method body's for you.
}

0

一个粗略的思考方式是:委托类型定义了将成为事件基础的“函数形状”。因此,ChangingHandler 是函数必须的样子。

进一步来说,委托实例就像是一个函数指针。这个概念可以从这个角度来理解。

public event ChangingHandler Change 定义了一个名为 Change 的函数指针,它将指向形状为 ChangingHandler 的函数。

但目前,这个函数指针没有指向任何东西。这就是 car.Change += new Car.ChangingHandler(car_Change) 这段代码的作用。

可以分两步来理解。委托不是普通的函数指针;它们实际上更像是函数指针堆栈,可以将多个函数添加到委托中。这就是为什么人们更常说“订阅”事件;将函数添加到事件中意味着当事件触发时将调用该函数。使用 += 运算符将函数添加到委托“堆栈”中。

你不能直接将一个函数添加到委托中,它必须以委托本身的形式表达。这是通过在同一行上创建一个new对象来完成的,尽管自从C# 2.0以来,我相信你可以直接添加函数而不需要调用new语法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接