C#Events如何在幕后工作?

Mat*_*att 40 c# events delegates .net-3.5

我正在使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,我明白的是,当一个人声明一个事件处理程序时

public EventHandler<EventArgs> ElementAddedEvent;
Run Code Online (Sandbox Code Playgroud)

它从未被初始化 - 所以,究竟是什么是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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我注意到有一个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
    }
}
Run Code Online (Sandbox Code Playgroud)

考虑到这一点让我意识到我并不真正理解在事件发生的情况下发生了什么 - 我想了解:)

Jon*_*eet 68

我在一篇文章中已经写了相当多的细节,但这里是摘要,假设你对代表自己感到满意:

  • 事件只是一个"添加"方法和一个"删除"方法,就像一个属性实际上只是一个"get"方法和一个"set"方法一样.(实际上,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<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);
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 在您的情况下生成的字段的初始值是null- 并且null如果删除所有订户,它将再次变为,这是Delegate.Remove的行为.

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

    public EventHandler<EventArgs> ElementAddedEvent = delegate {};
    
    Run Code Online (Sandbox Code Playgroud)

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

如果有任何事情仍然不清楚,请询问,我会尽力帮助!

  • @ dok1:我现在已经解释了几次事件:)这是C#,IMO最容易理解的领域之一. (3认同)
  • 乔恩·斯基特(Jon Skeet)在这里写得很扎实,用谷歌搜索了这个,然后首先找到了这个……正是我所需要的。 (2认同)