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

查看:151
本文介绍了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 + = 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)将代理附加到已存在的EventHandler。



I '我不知道如果我想要做什么将有助于...但最终我想在LINQ中提出一个抽象的父DataContext,其孩子可以注册他们想要的表类型 所以我可以有事件,如BeforeUpdate和AfterUpdate,但特定于类型。这样的东西:

  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类型,新字典< ChangeAction,EventHandler>());

EventHandler eventHandler = EventHandler.CreateDelegate(typeof(EventHandler),null,null)作为EventHandler;
m_ObservedTypes [type] .Add(ChangeAction.Insert,eventHandler);

eventHandler = EventHandler.CreateDelegate(typeof(EventHandler),null,null)作为EventHandler;
m_ObservedTypes [type] .Add(ChangeAction.Update,eventHandler);

eventHandler = EventHandler.CreateDelegate(typeof(EventHandler),null,null)作为EventHandler;
m_ObservedTypes [type] .Add(ChangeAction.Delete,eventHandler);
}
}

public static Dictionary< Type,Dictionary< ChangeAction,EventHandler>>事件
{
get {return m_ObservedTypes; }




public class MyClass
{
public MyClass()
{
BaseDataContext。事件[typeof(User)] [ChangeAction.Update] + =新的EventHandler(OnUserUpdate);
}

public void OnUserUpdated(object sender,EventArgs args)
{
//做某事
}
}

想到这一点让我意识到我不太明白事件发生了什么,我想理解:)

解决方案

我已经在一篇文章,但以下是摘要,假设您对代表本身:




  • 事件只是一个添加方法和删除方法,方式与属性只是一个get方法和set方法相同。 (实际上,CLI也允许使用raise / fire方法,但C#从不生成这个。)元数据描述了这个方法的引用。

  • 当你声明一个 类似场景的事件 (像您的ElementAddedEvent)编译器生成方法和私有字段(与委托的类型相同)。在课堂中,当您引用ElementAddedEvent时,您所指的是该字段。在课外,你是指该领域。

  • 当任何人订阅一个调用add方法的事件(使用+ =运算符)时。当他们取消订阅(使用 - =运算符)调用remove。

  • 对于类似场景的事件,有一些同步,但是否则add / remove只是调用Delegate。组合 / 删除以更改自动生成的字段的值。这两个操作都分配给支持字段 - 记住代表是不可变的。换句话说,自动生成代码非常像这样:

      //后缀字段
    //下划线只是看看这里发生了什么,让它更简单。
    //在这个类的其余源代码中,如果你引用
    // ElementAddedEvent,那么你真的是指这个字段。
    private EventHandler< EventArgs> __ElementAddedEvent;

    //实际事件
    public EventHandler< EventArgs> ElementAddedEvent
    {
    add
    {
    lock(this)
    {
    //等效于__ElementAddedEvent + = value;
    __ElementAddedEvent = Delegate.Combine(__ ElementAddedEvent,value);
    }
    }
    删除
    {
    lock(this)
    {
    //等效于__ElementAddedEvent - = value;
    __ElementAddedEvent = Delegate.Remove(__ ElementAddedEvent,value);
    }
    }
    }


  • 初始值在您的情况下生成的字段是 null - 如果所有订阅者被删除,它将永远成为 null ,如那就是Delegate.Remove的行为。


  • 如果你想要一个no-op处理程序订阅你的事件,以避免无效检查,你可以做:

      public EventHandler< EventArgs> ElementAddedEvent = delegate {}; 

    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天全站免登陆