C# 事件在幕后如何工作? [英] How do C# Events work behind the scenes?

查看:32
本文介绍了C# 事件在幕后如何工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的是 C#、.NET 3.5.我了解如何使用事件,如何在我的类中声明它们,如何从其他地方挂钩它们等等.一个人为的例子:

公共类MyList{私人列表<字符串>m_Strings = new List();public EventHandler元素添加事件;公共无效添加(字符串值){m_Strings.Add(value);if (ElementAddedEvent != null)ElementAddedEvent(value, EventArgs.Empty);}}[测试班]公共类 TestMyList{private bool m_Fired = false;[测试方法]公共无效测试事件(){MyList tmp = new MyList();tmp.ElementAddedEvent += new EventHandler(Fired);tmp.Add("测试");Assert.IsTrue(m_Fired);}私人无效发射(对象发送者,EventArgs args){m_Fired = 真;}}

然而,我理解的是当一个人声明一个事件处理程序

public EventHandler元素添加事件;

它从未被初始化 - 那么 ElementAddedEvent 究竟是什么?它指向什么?以下将不起作用,因为 EventHandler 从未初始化:

[测试类]公共类 TestMyList{private bool m_Fired = false;[测试方法]公共无效测试事件(){EventHandler有事发生;somethingHappend += new EventHandler(Fired);somethingHappend(this, EventArgs.Empty);Assert.IsTrue(m_Fired);}私人无效发射(对象发送者,EventArgs args){m_Fired = 真;}}

我注意到有一个 EventHandler.CreateDelegate(...),但所有方法签名都表明这仅用于通过典型的 ElementAddedEvent += new EventHandler(MyMethod) 将委托附加到现有的 EventHandler.

我不确定什么我正在尝试做的事情会有所帮助...但最终我想在 LINQ 中提出一个抽象的父 DataContext,它的孩子可以注册哪些表类型他们想要观察",所以我可以有诸如 BeforeUpdate 和 AfterUpdate 之类的事件,但特定于类型.像这样:

公共类 BaseDataContext : DataContext{私有静态字典<类型,字典<ChangeAction,EventHandler>>m_ObservedTypes = new Dictionary>();公共静态无效观察(类型类型){if (m_ObservedTypes.ContainsKey(type) == false){m_ObservedTypes.Add(type, new Dictionary());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>活动{得到 { 返回 m_ObservedTypes;}}}公开课 MyClass{公共 MyClass(){BaseDataContext.Events[typeof(User)][ChangeAction.Update] += new EventHandler(OnUserUpdate);}public void OnUserUpdated(对象发送者,EventArgs args){//做点什么}}

想到这让我意识到我并不真正了解事件背后发生了什么 - 我想了解 :)

解决方案

我在 一篇文章,但这里是摘要,假设您对 代表自己:

  • 事件只是一个add"方法和一个remove"方法,就像属性实际上只是一个get"方法和一个set"方法一样.(事实上​​,CLI 也允许使用raise/fire"方法,但 C# 永远不会生成这种方法.)元数据通过对方法的引用来描述事件.
  • 当您声明类似字段的事件(就像您的 ElementAddedEvent)编译器生成方法 和私有字段(与委托类型相同).在类中,当您引用 ElementAddedEvent 时,您指的是该字段.在课堂之外,您指的是该领域.
  • 当有人订阅调用 add 方法的事件(使用 += 运算符)时.当他们取消订阅时(使用 -= 运算符)调用删除.
  • 对于类似字段的事件,有一些同步,但除此之外,添加/删除只需调用 Delegate.组合/Remove 更改自动生成字段的值.这两个操作都分配给支持字段 - 请记住,委托是不可变的.换句话说,自动生成的代码很像这样:

    //支持字段//下划线只是让查看这里发生的事情变得更简单.//在该类的其余源代码中,如果您参考//ElementAddedEvent,你真的指的是这个字段.private EventHandler__ElementAddedEvent;//实际事件public EventHandler元素添加事件{添加{锁(这个){//相当于 __ElementAddedEvent += value;__ElementAddedEvent = Delegate.Combine(__ElementAddedEvent, value);}}消除{锁(这个){//相当于 __ElementAddedEvent -= value;__ElementAddedEvent = Delegate.Remove(__ElementAddedEvent, value);}}}

  • 在您的案例中,生成的字段的初始值为 null - 如果删除所有订阅者,它将始终再次变为 null,因为这是Delegate.Remove 的行为.

  • 如果您想要一个无操作"处理程序订阅您的事件,以避免无效检查,您可以这样做:

    public EventHandlerElementAddedEvent = 委托 {};

    delegate {} 只是一个匿名方法,它不关心它的参数,什么也不做.

如果还有什么不清楚的,请追问,我会尽力帮助!

I'm using 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? What does it point to? The following won't work, because the EventHandler is never initialized:

[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).

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. 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. (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.
  • 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). Within the class, when you refer to ElementAddedEvent you're referring to the field. Outside the class, you're referring to the field.
  • When anyone subscribes to an event (with the += operator) that calls the add method. 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.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.

  • 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.

If there's anything that's still unclear, please ask and I'll try to help!

这篇关于C# 事件在幕后如何工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆