C#事件如何在幕后工作?

我正在使用C#,.NET 3.5。 我知道如何利用事件,如何在我的课堂上宣布他们,如何把他们从别的地方挂钩等。一个人为的例子:

public class MyList { private List<string> m_Strings = new List<string>(); public EventHandler<EventArgs> ElementAddedEvent; public void Add(string value) { m_Strings.Add(value); if (ElementAddedEvent != null) ElementAddedEvent(value, EventArgs.Empty); } } [TestClass] public class TestMyList { private bool m_Fired = false; [TestMethod] public void TestEvents() { MyList tmp = new MyList(); tmp.ElementAddedEvent += new EventHandler<EventArgs>(Fired); tmp.Add("test"); Assert.IsTrue(m_Fired); } private void Fired(object sender, EventArgs args) { m_Fired = true; } } 

但是,我明白,是当一个声明一个事件处理程序

 public EventHandler<EventArgs> ElementAddedEvent; 

它从来没有初始化 – 那么ElementAddedEvent究竟是什么? 它指向什么? 以下将无法正常工作,因为EventHandler从未初始化:

 [TestClass] public class TestMyList { private bool m_Fired = false; [TestMethod] public void TestEvents() { EventHandler<EventArgs> somethingHappend; somethingHappend += new EventHandler<EventArgs>(Fired); somethingHappend(this, EventArgs.Empty); Assert.IsTrue(m_Fired); } private void Fired(object sender, EventArgs args) { m_Fired = true; } } 

我注意到有一个EventHandler.CreateDelegate(…),但所有的方法签名表明,这只是用于通过典型的ElementAddedEvent + = new EventHandler(MyMethod)将Delegates附加到已经存在的EventHandler。

我不知道如果我想要做什么会帮助…但最终我想在LINQ中想出一个抽象的父DataContext,他们的孩子可以注册哪些表types,他们希望“观察”,所以我可以有事件如BeforeUpdate和AfterUpdate,但特定于types。 像这样的东西:

 public class BaseDataContext : DataContext { private static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> m_ObservedTypes = new Dictionary<Type, Dictionary<ChangeAction, EventHandler>>(); public static void Observe(Type type) { if (m_ObservedTypes.ContainsKey(type) == false) { m_ObservedTypes.Add(type, new Dictionary<ChangeAction, EventHandler>()); EventHandler eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler; m_ObservedTypes[type].Add(ChangeAction.Insert, eventHandler); eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler; m_ObservedTypes[type].Add(ChangeAction.Update, eventHandler); eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler; m_ObservedTypes[type].Add(ChangeAction.Delete, eventHandler); } } public static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> Events { get { return m_ObservedTypes; } } } public class MyClass { public MyClass() { BaseDataContext.Events[typeof(User)][ChangeAction.Update] += new EventHandler(OnUserUpdate); } public void OnUserUpdated(object sender, EventArgs args) { // do something } } 

思考这个让我意识到我不是很了解发生在事件之下的事情 – 我想明白:)

我已经在一篇文章中写了相当详细的内容 ,但这里是总结,假设你对代表自己感到满意:

  • 事件只是一个“add”方法和一个“remove”方法,就像一个属性实际上只是一个“get”方法和一个“set”方法一样。 (实际上,CLI也允许使用“raise / fire”方法,但是C#从不会生成这个方法。)元数据通过引用方法来描述事件。
  • 当你声明一个类似字段的事件 (比如你的ElementAddedEvent)时,编译器会生成方法和一个私有字段 (与委托types相同)。 在类中,当引用ElementAddedEvent时,您指的是该字段。 课外,你指的是这个领域。
  • 当有人订阅调用add方法的事件(带+ =运算符)时。 当他们取消订阅(与 – =运营商),调用删除。
  • 对于类似字段的事件,有一些同步,但否则添加/删除只是调用委托。 合并 / 删除以更改自动生成的字段的值。 这两个操作都分配给后台字段 – 记住代理是不可变的。 换句话说,自动生成的代码非常像这样:

     // Backing field // The underscores just make it simpler to see what's going on here. // In the rest of your source code for this class, if you refer to // ElementAddedEvent, you're really referring to this field. private EventHandler<EventArgs> __ElementAddedEvent; // Actual event public EventHandler<EventArgs> ElementAddedEvent { add { lock(this) { // Equivalent to __ElementAddedEvent += value; __ElementAddedEvent = Delegate.Combine(__ElementAddedEvent, value); } } remove { lock(this) { // Equivalent to __ElementAddedEvent -= value; __ElementAddedEvent = Delegate.Remove(__ElementAddedEvent, value); } } } 
  • 在你的情况下,生成的字段的初始值是null – 如果所有的用户都被删除了,那么它总是会变成null ,因为这是Delegate.Remove的行为。

  • 如果你想要一个“no-op”处理程序来订阅你的事件,为了避免无效性检查,你可以这样做:

     public EventHandler<EventArgs> ElementAddedEvent = delegate {}; 

    delegate {}只是一个不关心其参数的匿名方法,并且什么也不做。

如果还有什么不清楚的地方,请问,我会尽力帮忙!

在引擎盖下,事件只是具有特殊调用约定的代表。 (例如,在举办活动之前,您不必检查无效。)

在伪代码中,Event.Invoke()如下所示:

如果事件具有监听器以任意顺序在该线程上同步调用每个监听器。

由于事件是多播的,他们将有零个或多个听众,在一个集合中举行。 CLR将通过它们循环,以任意顺序调用每一个。

需要记住的一点是事件处理程序在事件引发的同一线程中执行。将它们视为产生新线程是一种常见的心理错误。 他们不。