如何C#事件的幕后工作?幕后、事件、工作

2023-09-08 08:44:13 作者:余生我再牵你的手

我使用C#,.NET 3.5。我明白如何利用事件,如何声明它们在我的课,如何从别的地方,他们挂钩等。人为的例子:

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;

这是从来没有初始化 - 所以,究竟是什么,是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;
    }
}

我注意到,有一个EventHandler.CreateDelegate(...),但所有的方法签名表明这仅用于通过典型ElementAddedEvent + =新的事件处理程序(的MyMethod)代表连接到已经存在的事件处理程序。

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

我不知道的什么的我试图做会帮助......但最终我想拿出一个抽象父的DataContext的LINQ其子女可以注册表类型他们想要的观察这样我就可以有这样的更新前和更新后的事件,但具体到型号。事情是这样的:

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

思考这让我意识到我真的不明白什么HOD下发生的事情与活动 - 我想明白了:)

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:

的事件仅仅是一个增加的方法和删除的方法,以同样的方式,一个属性是实际上只是一个获取方法和集的方法。 (事实上​​,CLI允许一个升/火的方法为好,但C#从来没有产生这个。)元数据描述了事件引用的方法。 在声明的 现场般的事件 的(如您ElementAddedEvent)编译器生成的方法的和(同类型的代表)的私人领域的。在类中,当您参考ElementAddedEvent你指到外地。在类外,你指到外地。 当有人赞同事件(与+ =运算符)调用add方法。当他们取消(使用 - =运算符)调用删除。

有关字段的事件,有一些同步,但否则添加/删除只需调用委托。的联合 / 删除更改值自动生成的场。这两个操作都分配给支持字段 - 记住,代表是不可改变的。换句话说,自动生成的code是非常喜欢这样的: 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);
        }
    }
}

产生磁场中的情况下,初始值为 - 它总是会成为再次,如果所有用户都去掉了,因为这是Delegate.Remove的行为。

幕后故事 表白所有默默付出的幕后工作者

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!