一尘不染

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

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他们的孩子可以注册自己想要的表类型“观察”这样我就可以有事件例如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(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
    }
}

考虑到这一点,使我意识到我不太了解事件发生后的状况-我想理解:)


阅读 385

收藏
2020-05-19

共1个答案

一尘不染

我已经在文章中详细介绍了这一点,但是这里是总结,假设您对委托人自己很满意:

  • 事件实际上是“添加”方法和“删除”方法,就像属性实际上只是“获取”方法和“设置”方法一样。(实际上,CLI也允许使用“ raise / fire”方法,但是C#从未生成此方法。)元数据通过引用方法来描述事件。
  • 当您声明类似 字段的事件 (例如ElementAddedEvent)时,编译器将生成方法 和私有字段 (与委托的类型相同)。在类中,当您引用ElementAddedEvent时,您是在引用字段。在课堂之外,您指的是领域。
  • 任何人订阅使用add方法的事件(使用+ =运算符)时。当他们退订(使用-=运算符)时,将调用remove。
  • 对于类似字段的事件,需要进行一些同步,否则添加/删除仅调用Delegate。合并 / 删除以更改自动生成的字段的值。这两个操作都分配给后备字段-请记住,委托是不可变的。换句话说,自动生成的代码非常像这样:

    // 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 __ElementAddedEvent;

    // Actual event
    public EventHandler 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的行为。

  • 如果您希望“无操作”处理程序订阅您的事件,以避免无效检查,则可以执行以下操作:

    public EventHandler<EventArgs> ElementAddedEvent = delegate {};
    

delegate {}只是它不关心它的参数,所以没有任何一个匿名方法。

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

2020-05-19