Java或C#中的事件/代理
我一直在努力学习事件/代表,但我对两者之间的关系感到困惑。 我知道委托允许你调用不同的函数,而不需要知道调用什么特定的函数。 (例如:图形函数需要接受要绘制的不同函数的输入)。
但我没有看到代表如何在事件中使用。
有人可以构建一个简单的示例(伪代码或C#或Java),说明代理与事件相关的工作方式吗?
谢谢!
(这完全来自C#的观点。)
我有一篇关于事件和代表之间差异的文章 。 这更详细地涵盖了下面提到的所有内容。
基本上我喜欢把事件想象成一个属性 – 它是一对方法,就是这样。 而不是获取/设置,事件已添加/删除 – 意味着“添加此事件处理程序”和“删除此事件处理程序”。 所有事件都是核心。
C#也有类似字段的事件 ,这是一个快捷方式:
public event EventHandler Foo;
声明一个字段和一个事件,具有几乎无足轻重的添加/删除实现。 在课堂上,指Foo
指的是该领域。 在课外,指Foo
指的是事件。
基本思想是,通过传入委托(事件处理程序 ),事件允许其他代码订阅和取消订阅。 通常,通过创建包含先前事件处理程序列表和新事件处理程序列表的新多播委托来实现订阅。 因此,如果您将事件处理程序存储在名为myEventHandlers
的字段中,则订阅实现可能是:
myEventHandlers += value;
类似地,取消订阅通常涉及在没有指定处理程序的情况下创建新的多播委托:
myEventHandlers -= value;
然后,当您想要引发/触发事件时,您只需调用该多播委托 – 通常使用无效检查以避免在没有订阅的情况下抛出exception:
EventHandler handler = myEventHandlers; if (handler != null) { // You could pass in a different "sender" and "args" of course handler(this, EventArgs.Empty); }
使用事件,订阅者彼此不了解,并且不能自己(通常)提出事件。 换句话说,它是一种封装模式,在语言和平台中都被赋予了地位。
您需要具体说明您想要的语言。 据我所知,Java没有委托的概念(虽然我可能完全错了); 它往往遵循观察者模式进行事件处理。
但是,C#确实如此。 C#中的event
与委托具有相同的关系,因为属性具有其后备字段。 委托本身是存储指向处理事件的函数的指针(或者更确切地说,是附加到事件的指针列表;我在这里松散地使用术语“指针”)。
如果我在C#中声明:
public event EventHandler MyEvent;
并像这样调用事件:
MyEvent(this, EventArgs.Empty);
它实际上只是完整事件实现的一些简写:
private EventHandler myEventHandler; public event EventHandler MyEvent { add { myEventHandler += value; } remove { myEventHandler -= value; } }
并称之为……
myEventHandler(this, EventArgs.Empty);
所有这一切都是说实际event
暴露了两个操作: add
和remove
消费代码使用它们将事件处理程序附加到事件。 在默认(简写)表示法中,编译器创建委托类型的私有实例成员,并以我上面描述的方式使用它。 当您“调用”事件时,编译器实际上会将事件名称替换为它创建的私有后代委托的名称。 这就是您无法从子类调用event
的原因 – 如果事件是以速记方式创建的,则后备成员是private
。
差异很简单。
delegate
是一个包含两个字段的类 – object和MethodInfo。
event
是类型delegate
的私有字段, add
和remove
两个公共方法。
通常在事件的引擎下使用MulticastDelegate
– 它是一个inheritance自Delegate
并包含Delegates列表的类。 这允许事件具有多个订户。
您可以查看: http : //msdn.microsoft.com/en-us/library/17sde2xt.aspx
这个例子在这里继续: http : //msdn.microsoft.com/en-us/library/xwbwks95.aspx
基本上,正如前面提到的,事件只是委托的特殊情况,但是随着.NET 3.5的变化,您可以在不使用委托的情况下编写事件,尽管代码仍在编写。
如果你看看这篇文章,他们将展示如何使用lambda表达式和事件的匿名函数: http : //msdn.microsoft.com/en-us/library/ms366768.aspx
.Net事件只是代言人:它们在编译器中提供了一些语法糖。
您可以设置/重置委托,但只能添加或删除事件处理程序。 理由是您不会关心其他人订阅事件,而普通代表更多地用于“回调”场景。
但在所有事情的最后,他们非常相似。
一些资源:
C#活动与代表
代表和活动 – 简短的问答
我是java世界的新手,但我不得不承认我很高兴,但我仍然想念一些C#的东西,所以设计这个模式给了我很好的结果,Java专家看到使用这个模式的一些缺点? 它只支持java 8:
@FunctionalInterface public interface IEvent { void invoke(TEventArgs eventArgs); } public class EventHandler { private ArrayList> eventDelegateArray = new ArrayList<>(); public void subscribe(IEvent methodReference) { eventDelegateArray.add(methodReference); } public void unSubscribe(IEvent methodReference) { eventDelegateArray.remove(methodReference); } public void invoke(TEventArgs eventArgs) { if (eventDelegateArray.size()>0) eventDelegateArray.forEach(p -> p.invoke(eventArgs)); } } public class DummyEventProducer { // The event public EventHandler myEvent = new EventHandler<>(); public void onMyEvent(String A) { myEvent.invoke(A); } } public class DummySubscriber { // The method will be subscribed to the event public void methodCallWhenEventGetTriggered(String eventArgs) { System.out.println("event fired with eventargs: " + eventArgs); } } public class Main { public static void main(String[] args) { // A dummy producer DummyEventProducer producer = new DummyEventProducer(); // A dummy subscribers DummySubscriber testingInstanceA = new DummySubscriber(); DummySubscriber testingInstanceB = new DummySubscriber(); DummySubscriber testingInstanceC = new DummySubscriber(); // We create decoupled event links because we want to un-subscribe later IEvent EventSink1 = testingInstanceA::methodCallWhenEventGetTriggered; IEvent EventSink2 = testingInstanceB::methodCallWhenEventGetTriggered; IEvent EventSink3 = testingInstanceC::methodCallWhenEventGetTriggered; // subscribe to the event on dummy producer producer.myEvent.subscribe(EventSink1); producer.myEvent.subscribe(EventSink2); producer.myEvent.subscribe(EventSink3); // fire the event on producer producer.onMyEvent("Hola MUNDO with decoupled subscriptions!"); // unsubscribe to the event on dummy producer producer.myEvent.unSubscribe(EventSink1); producer.myEvent.unSubscribe(EventSink2); producer.myEvent.unSubscribe(EventSink3); // fire the event on producer again producer.onMyEvent("Hola MUNDO! with no events subscriptions :("); // IF YOU DON CARE ABOUT UNSUBSCRIBE YOU CAN LINK EVENTS DIRECTLY TO THE SUBSCRIBER producer.myEvent.subscribe(testingInstanceA::methodCallWhenEventGetTriggered); producer.myEvent.subscribe(testingInstanceB::methodCallWhenEventGetTriggered); producer.myEvent.subscribe(testingInstanceC::methodCallWhenEventGetTriggered); // fire the event on producer again producer.onMyEvent("Hola MUNDO! with strong link subscriptions (cannot be un-subscribed"); } }
随意问,更正,建议=)最好的问候!