简体   繁体   English

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

[英]How do C# Events work behind the scenes?

I'm using C#, .NET 3.5.我正在使用 C#、.NET 3.5。 I understand how to utilize events, how to declare them in my class, how to hook them from somewhere else, etc. A contrived example:我了解如何利用事件,如何在我的班级中声明它们,如何从其他地方挂钩它们等等。一个人为的例子:

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;
    }
}

However, what I do not understand, is when one declares an event handler然而,我明白的是,当一个人声明一个事件处理程序时

public EventHandler<EventArgs> ElementAddedEvent;

It's never initialized - so what, exactly, is ElementAddedEvent?它从未被初始化——那么到底什么是 ElementAddedEvent? What does it point to?它指向什么? The following won't work, because the EventHandler is never initialized:以下将不起作用,因为 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;
    }
}

I notice that there is an EventHandler.CreateDelegate(...), but all the method signatures suggest this is only used for attaching Delegates to an already existing EventHandler through the typical ElementAddedEvent += new EventHandler(MyMethod).我注意到有一个 EventHandler.CreateDelegate(...),但所有方法签名都表明这仅用于通过典型的 ElementAddedEvent += new EventHandler(MyMethod) 将委托附加到已经存在的 EventHandler。

I'm not sure if what I am trying to do will help... but ultimately I'd like to come up with an abstract parent DataContext in LINQ whose children can register which table Types they want "observed" so I can have events such as BeforeUpdate and AfterUpdate, but specific to types.我不知道什么,我试图做将帮助......但最终我想拿出在LINQ一个抽象父的DataContext他们的孩子可以注册自己想要的表类型“观察”这样我就可以有事件例如 BeforeUpdate 和 AfterUpdate,但特定于类型。 Something like this:像这样的东西:

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
    }
}

Thinking about this made me realize I don't really understand what's happening under the hod with events - and I would like to understand :)考虑到这一点,我意识到我并不真正了解事件背后发生了什么 - 我想了解 :)

I've written this up in a fair amount of detail in an article , but here's the summary, assuming you're reasonably happy with delegates themselves:我已经在一篇文章中详细描述了这一点,但这里是摘要,假设您对代表本身感到满意:

  • An event is just an "add" method and a "remove" method, in the same way that a property is really just a "get" method and a "set" method.事件只是一个“add”方法和一个“remove”方法,就像属性实际上只是一个“get”方法和一个“set”方法一样。 (In fact, the CLI allows a "raise/fire" method as well, but C# never generates this.) Metadata describes the event with references to the methods. (实际上,CLI 也允许使用“raise/fire”方法,但 C# 永远不会生成此方法。)元数据通过对方法的引用来描述事件。
  • When you declare a field-like event (like your ElementAddedEvent) the compiler generates the methods and a private field (of the same type as the delegate).当您声明一个类似字段的事件(如您的 ElementAddedEvent)时,编译器会生成方法和一个私有字段(与委托类型相同)。 Within the class, when you refer to ElementAddedEvent you're referring to the field.在类中,当您引用 ElementAddedEvent 时,您指的是该字段。 Outside the class, you're referring to the field.在课堂之外,您指的是该领域。
  • When anyone subscribes to an event (with the += operator) that calls the add method.当任何人订阅调用 add 方法的事件(使用 += 运算符)时。 When they unsubscribe (with the -= operator) that calls the remove.当他们取消订阅时(使用 -= 运算符)调用删除。
  • For field-like events, there's some synchronization but otherwise the add/remove just call Delegate.对于类似字段的事件,有一些同步,但除此之外,添加/删除只需调用 Delegate。 Combine / Remove to change the value of the auto-generated field. 合并/ 删除以更改自动生成字段的值。 Both of these operations assign to the backing field - remember that delegates are immutable.这两个操作都分配给支持字段 - 请记住,委托是不可变的。 In other words, the autogenerated code is very much like this:换句话说,自动生成的代码非常像这样:

     // 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); } } }
  • The initial value of the generated field in your case is null - and it will always become null again if all subscribers are removed, as that is the behaviour of Delegate.Remove.在您的情况下,生成的字段的初始值为null - 如果删除所有订阅者,它将始终再次变为null ,因为这是 Delegate.Remove 的行为。

  • If you want a "no-op" handler to subscribe to your event, so as to avoid the nullity check, you can do:如果您希望“无操作”处理程序订阅您的事件,以避免无效检查,您可以执行以下操作:

     public EventHandler<EventArgs> ElementAddedEvent = delegate {};

    The delegate {} is just an anonymous method which doesn't care about its parameters and does nothing. delegate {}只是一个匿名方法,它不关心它的参数并且什么都不做。

If there's anything that's still unclear, please ask and I'll try to help!如果还有什么不清楚的,请追问,我会尽力提供帮助!

Under the hood, events are just delegates with special calling conventions.在幕后,事件只是具有特殊调用约定的委托。 (For example, you don't have to check for nullity before raising an event.) (例如,您不必在引发事件之前检查无效性。)

In pseudocode, Event.Invoke() breaks down like this:在伪代码中, Event.Invoke() 分解如下:

If Event Has Listeners Call each listener synchronously on this thread in arbitrary order.如果事件有侦听器以任意顺序在此线程上同步调用每个侦听器。

Since events are multicast, they will have zero or more listeners, held in a collection.由于事件是多播的,它们将有零个或多个侦听器,保存在一个集合中。 The CLR will loop through them, calling each in an arbitrary order. CLR 将遍历它们,以任意顺序调用它们。

One big caveat to remember is that event handlers execute in the same thread as the event is raised in. It's a common mental error to think of them as spawning a new thread.要记住的一个重要警告是,事件处理程序与引发事件的线程在同一线程中执行。将它们视为产生新线程是一种常见的心理错误。 They do not.他们不。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM