我理解事件的目的,特别是在创建用户界面的上下文中。我认为这是创建事件的原型:
public void EventName(object sender, EventArgs e);
事件处理程序做什么,为什么需要它们,以及如何创建一个?
我理解事件的目的,特别是在创建用户界面的上下文中。我认为这是创建事件的原型:
public void EventName(object sender, EventArgs e);
事件处理程序做什么,为什么需要它们,以及如何创建一个?
当前回答
我最近做了一个如何在c#中使用事件的例子,并发布在我的博客上。我用了一个简单的例子,尽量把它讲清楚。如果它可能帮助任何人,这里是:http://www.konsfik.com/using-events-in-csharp/
它包括描述和源代码(带有大量注释),主要关注事件和事件处理程序的正确使用(类似模板)。
一些要点是:
Events are like "sub - types of delegates", only more constrained (in a good way). In fact an event's declaration always includes a delegate (EventHandlers are a type of delegate). Event Handlers are specific types of delegates (you may think of them as a template), which force the user to create events which have a specific "signature". The signature is of the format: (object sender, EventArgs eventarguments). You may create your own sub-class of EventArgs, in order to include any type of information the event needs to convey. It is not necessary to use EventHandlers when using events. You may completely skip them and use your own kind of delegate in their place. One key difference between using events and delegates, is that events can only be invoked from within the class that they were declared in, even though they may be declared as public. This is a very important distinction, because it allows your events to be exposed so that they are "connected" to external methods, while at the same time they are protected from "external misuse".
其他回答
c#有两个术语,委托和事件。让我们从第一个开始。
委托
委托是对方法的引用。就像你可以创建一个实例的引用:
MyClass instance = myFactory.GetInstance();
你可以使用委托来创建一个方法的引用:
Action myMethod = myFactory.GetInstance;
现在你有了一个方法的引用,你可以通过引用调用这个方法:
MyClass instance = myMethod();
但你为什么要这么做?你也可以直接调用myFactory.GetInstance()。在这种情况下你可以。但是,有很多情况需要考虑,您不希望应用程序的其余部分了解myFactory或直接调用myFactory. getinstance()。
一个明显的例子是,如果你想从一个中心位置(也就是工厂方法模式)将myFactory.GetInstance()替换为myOfflineFakeFactory.GetInstance()。
工厂方法模式
所以,如果你有一个TheOtherClass类,它需要使用myFactory. getinstance(),这是没有委托的代码看起来的样子(你需要让TheOtherClass知道你的myFactory的类型):
TheOtherClass toc;
//...
toc.SetFactory(myFactory);
class TheOtherClass
{
public void SetFactory(MyFactory factory)
{
// set here
}
}
如果你使用委托,你不需要暴露我的工厂的类型:
TheOtherClass toc;
//...
Action factoryMethod = myFactory.GetInstance;
toc.SetFactoryMethod(factoryMethod);
class TheOtherClass
{
public void SetFactoryMethod(Action factoryMethod)
{
// set here
}
}
因此,您可以将委托交给其他类使用,而无需向它们暴露您的类型。你唯一要暴露的是你的方法的签名(你有多少参数等等)。
"我的方法的签名"我在哪儿听过这个?哦,是的,接口!!接口描述了整个类的签名。把委托看作是只描述一个方法的签名!
接口和委托之间的另一个巨大区别是,当你编写类时,你不必对c#说“这个方法实现了那种类型的委托”。对于接口,你确实需要说“这个类实现了那种类型的接口”。
此外,一个委托引用可以(有一些限制,见下文)引用多个方法(称为MulticastDelegate)。这意味着当您调用委托时,将执行多个显式附加的方法。一个对象引用总是只能引用一个对象。
MulticastDelegate的限制是(方法/委托)签名不应该有任何返回值(void),关键字out和ref不能在签名中使用。显然,您不能调用两个返回一个数字的方法,并期望它们返回相同的数字。一旦签名符合要求,该委托就自动成为MulticastDelegate。
事件
事件只是属性(就像get;set;属性到实例字段),这将从其他对象向委托公开订阅。但是,这些属性不支持get;set;。相反,它们支持add;删除;
所以你可以有:
Action myField;
public event Action MyProperty
{
add { myField += value; }
remove { myField -= value; }
}
在UI中的使用(WinForms,WPF,UWP等等)
So, now we know that a delegate is a reference to a method and that we can have an event to let the world know that they can give us their methods to be referenced from our delegate, and we are a UI button, then: we can ask anyone who is interested in whether I was clicked, to register their method with us (via the event we exposed). We can use all those methods that were given to us and reference them by our delegate. And then, we'll wait and wait.... until a user comes and clicks on that button, then we'll have enough reason to invoke the delegate. And because the delegate references all those methods given to us, all those methods will be invoked. We don't know what those methods do, nor we know which class implements those methods. All we do care about is that someone was interested in us being clicked, and gave us a reference to a method that complied with our desired signature.
Java
像Java这样的语言没有委托。相反,他们使用接口。他们这样做的方式是让任何对“我们被点击”感兴趣的人来实现某个接口(使用我们可以调用的某个方法),然后给我们实现该接口的整个实例。我们保留了实现该接口的所有对象的列表,并在我们被单击时调用它们的“我们可以调用的特定方法”。
发布者:事件发生的地方。发布者应该指定类使用哪个委托,并生成必要的参数,将这些参数和自身传递给委托。
订阅者:响应发生的地方。订阅者应指定响应事件的方法。这些方法应该采用与委托相同类型的参数。订阅者然后将此方法添加到发布者的委托。
因此,当事件在publisher中发生时,delegate将收到一些事件参数(数据等),但publisher不知道所有这些数据将会发生什么。订阅者可以在自己的类中创建方法来响应发布者类中的事件,这样订阅者就可以响应发布者的事件。
只是在这里添加到现有的伟大答案-在已接受的代码的基础上构建,它使用了委托void MyEventHandler(string foo)…
因为编译器知道SomethingHappened事件的委托类型,所以:
myObj.SomethingHappened += HandleSomethingHappened;
完全等价于:
myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);
处理程序也可以像这样用-=取消注册:
// -= removes the handler from the event's list of "listeners":
myObj.SomethingHappened -= HandleSomethingHappened;
为了完整起见,只能在拥有该事件的类中,像这样触发事件:
//Firing the event is done by simply providing the arguments to the event:
var handler = SomethingHappened; // thread-local copy of the event
if (handler != null) // the event is null if there are no listeners!
{
handler("Hi there!");
}
处理程序的线程本地副本是必要的,以确保调用是线程安全的——否则,线程可能会在检查事件是否为空后立即注销该事件的最后一个处理程序,我们将在那里有一个“有趣的”NullReferenceException。
c# 6为这个模式引入了一个很好的简写。它使用空传播操作符。
SomethingHappened?.Invoke("Hi there!");
这实际上是一个事件处理程序的声明——一个在触发事件时被调用的方法。要创建一个事件,你可以这样写:
public class Foo
{
public event EventHandler MyEvent;
}
然后你可以像这样订阅这个事件:
Foo foo = new Foo();
foo.MyEvent += new EventHandler(this.OnMyEvent);
OnMyEvent()的定义如下:
private void OnMyEvent(object sender, EventArgs e)
{
MessageBox.Show("MyEvent fired!");
}
每当Foo触发MyEvent,那么你的OnMyEvent处理程序将被调用。
你不必总是使用EventArgs的实例作为第二个参数。如果你想包含额外的信息,你可以使用一个从EventArgs派生的类(根据惯例EventArgs是基类)。例如,如果你查看WinForms中的Control上定义的一些事件,或者WPF中的FrameworkElement上定义的一些事件,你可以看到将附加信息传递给事件处理程序的事件示例。
//This delegate can be used to point to methods
//which return void and take a string.
public delegate void MyDelegate(string foo);
//This event can cause any method which conforms
//to MyEventHandler to be called.
public event MyDelegate MyEvent;
//Here is some code I want to be executed
//when SomethingHappened fires.
void MyEventHandler(string foo)
{
//Do some stuff
}
//I am creating a delegate (pointer) to HandleSomethingHappened
//and adding it to SomethingHappened's list of "Event Handlers".
myObj.MyEvent += new MyDelegate (MyEventHandler);