C#活动如何在幕后工作? [英] How do C# Events work behind the scenes?
问题描述
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 becomenull
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屋!