C# 事件如何在后台工作
本文关键字:后台 工作 事件 | 更新日期: 2023-09-27 17:47:20
我正在使用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;
它从未初始化过 - 那么,ElementAddEvent到底是什么?它指向什么? 以下内容不起作用,因为事件处理程序永远不会初始化:
[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(...),但所有的方法签名都表明它仅用于通过典型的 ElementAddEvent += new EventHandler(MyMethod) 将委托附加到已经存在的 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
}
}
想到这一点让我意识到我并不真正了解事件下发生了什么——我想了解:)
我在一篇文章中写得相当详细,但这里是摘要,假设你对代表本身相当满意:
- 事件只是一个"add"方法和一个"
remove"方法,就像属性实际上只是一个"get"方法和一个"set"方法一样。(事实上,CLI 也允许"提升/触发"方法,但 C# 从不生成此方法。元数据通过对方法的引用来描述事件。
当您声明类似字段的事件(如 ElementAddEvent)时,编译器会生成方法和私有字段(与委托的类型相同)。在类中,当您引用 ElementAddEvent 时,您指的是字段。在课堂之外,你指的是领域。
当任何人订阅调用 add 方法的事件(使用 += 运算符)时。当他们取消订阅(使用 -= 运算符)调用删除时。
对于类似字段的事件,会有一些同步,否则添加/删除只需调用 Delegate.Combine/Remove 即可更改自动生成字段的值。这两个操作都分配给支持字段 - 请记住,委托是不可变的。换句话说,自动生成的代码非常像这样:
// 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); } } }
在您的情况下,生成的字段的初始值是
null
- 如果删除所有订阅者,它将始终再次变为null
,因为这是 Delegate.Remove 的行为。如果您希望"no-op"处理程序订阅您的事件,以避免无效性检查,您可以执行以下操作:
public EventHandler<EventArgs> ElementAddedEvent = delegate {};
delegate {}
只是一个匿名方法,它不关心其参数,也不做任何事情。
如果还有什么不清楚的地方,请问,我会尽力帮忙的!
在引擎盖下,事件只是具有特殊调用约定的委托。(例如,在引发事件之前不必检查是否为空。
在伪代码中,Event.Invoke() 分解如下:
如果事件具有侦听器 以任意顺序在此线程上同步调用每个侦听器。
由于事件是多播的,因此它们将具有零个或多个侦听器,这些侦听器保存在集合中。CLR 将遍历它们,以任意顺序调用每个。
要记住的一个重要警告是,事件处理程序在引发事件的同一线程中执行。将它们视为产生新线程是一个常见的心理错误。他们没有。