c# – .NET对象事件和处理/ GC
编辑:在Joel Coehoorns之后,我明白我需要更加具体,所以我修改了我的代码来更接近我想要理解的东西…
事件:据了解,在后台,事件是EventHandlers的“收集”,也就是事件发生时将被执行的代表.所以对我来说,这意味着如果对象Y有事件E,对象X订阅事件YE,那么Y将引用X,因为Y必须执行位于X中的方法,这样就不能收集X,我明白了 //Creates reference to this (b) in a. a.EventHappened += new EventHandler(this.HandleEvent); 但是,Joel Coehoorn不是这么说的
我不明白X将如何引用Y? 我修改了一个例子来说明我的情况更加接近: class Service //Let's say it's windows service that must be 24/7 online { A _a; void Start() { CustomNotificationSystem.OnEventRaised += new EventHandler(CustomNotificationSystemHandler) _a = new A(); B b1 = new B(_a); B b2 = new B(_a); C c1 = new C(_a); C c2 = new C(_a); } void CustomNotificationSystemHandler(args) { //_a.Dispose(); ADDED BY **EDIT 2*** a.Dispose(); _a = new A(); /* b1,b2,c1,c2 will continue to exists as is,and I know they will now subscribed to previous instance of _a,and it's OK by me,BUT in that example,now,nobody references the previous instance of _a (b not holds reference to _a) and by my theory,previous instance of _a,now may be collected...or I'm missing something??? */ } } class A : IDisposable { public event EventHandler EventHappened; } class B { public B(A a) //Class B does not stores reference to a internally. { a.EventHappened += new EventHandler(this.HandleEventB); } public void HandleEventB(object sender,EventArgs args) { } } class C { public C(A a) //Class B not stores reference to a internally. { a.EventHappened += new EventHandler(this.HandleEventC); } public void HandleEventC(object sender,EventArgs args) { } } 编辑2:好的,现在很清楚,当订阅者订阅发布者的事件时,它不会创建对订阅者中的发布者的引用.只有从发布商到订阅者的引用(通过EventHandler)…在这种情况下,当发布者在订阅者(订阅者生命周期大于发布者)之前收集发布者时,没有问题. 但是,如我所知,GC不会保证在理论上收集发布者,即使订阅者的生命周期比发布商更大,可能会发生订阅者合法收集,但是发布商仍然没有收集(我没有收到)不知道在最接近的GC周期内,GC将足够聪明地收集发布者,然后再收到用户. 无论如何,在这种情况下,由于我的用户没有直接引用发布商,不能取消订阅该事件,所以我想让发布商实施IDisposable,以便在删除所有引用他之前处理它(参见CustomNotificationSystemHandler in我的例子). 再次,我应该在发布商中写什么来处理方法,以清除所有对订阅者的引用?应该是EventHappened – = null;或EventHappened = null;或者没有办法这样做,我需要做下面的东西? public event EventHandler EventHappened { add { eventTable["EventHappened"] = (EventHandler)eventTable["EventHappened"] + value; } remove { eventTable["EventHappened"] = (EventHandler)eventTable["EventHappened"] - value; } } 解决方法
我在我的示例代码中添加了我的评论.
class A : IDisposable { public event EventHandler EventHappened { add { eventTable["EventHappened"] = (EventHandler)eventTable["EventHappened"] + value; } remove { eventTable["EventHappened"] = (EventHandler)eventTable["EventHappened"] - value; } } public void Dispose() { //Amit: If you have only one event 'EventHappened',//you can clear up the subscribers as follows eventTable["EventHappened"] = null; //Amit: EventHappened = null will not work here as it is //just a syntactical sugar to clear the compiler generated backing delegate. //Since you have added 'add' and 'remove' there is no compiler generated //delegate to clear // //Above was just to explain the concept. //If eventTable is a dictionary of EventHandlers //You can simply call 'clear' on it. //This will work even if there are more events like EventHappened } } class B { public B(A a) { a.EventHappened += new EventHandler(this.HandleEventB); //You are absolutely right here. //class B does not store any reference to A //Subscribing an event does not add any reference to publisher //Here all you are doing is calling 'Add' method of 'EventHappened' //passing it a delegate which holds a reference to B. //Hence there is a path from A to B but not reverse. } public void HandleEventB(object sender,EventArgs args) { } } class C { public C(A a) { a.EventHappened += new EventHandler(this.HandleEventC); } public void HandleEventC(object sender,EventArgs args) { } } class Service { A _a; void Start() { CustomNotificationSystem.OnEventRaised += new EventHandler(CustomNotificationSystemHandler) _a = new A(); //Amit:You are right all these do not store any reference to _a B b1 = new B(_a); B b2 = new B(_a); C c1 = new C(_a); C c2 = new C(_a); } void CustomNotificationSystemHandler(args) { //Amit: You decide that _a has lived its life and must be disposed. //Here I assume you want to dispose so that it stops firing its events //More on this later _a.Dispose(); //Amit: Now _a points to a brand new A and hence previous instance //is eligible for collection since there are no active references to //previous _a now _a = new A(); } }
正如我在上述代码中的评论所解释的,你没有在这里遗漏任何东西:)
由于发布商引用订阅者,所以在发布商之前,订阅者无法收到资格,但是倒序可能是真实的.如果发布者在订阅者之前收集,正如你所说,没有问题.如果订阅者属于比发布商更低的GC代码,则由于发布者持有对订阅者的引用,GC将把订阅者视为可达到并且不会收集.如果两者都属于同一代人,他们将被收集在一起.
与某些建议相反,如果您确定确定不再需要该对象,我建议您执行处理.简单地更新对象引用可能并不总是导致对象停止发布事件. 请考虑以下代码: class MainClass { public static Publisher Publisher; static void Main() { Publisher = new Publisher(); Thread eventThread = new Thread(DoWork); eventThread.Start(); Publisher.StartPublishing(); //Keep on firing events } static void DoWork() { var subscriber = new Subscriber(); subscriber = null; //Subscriber is referenced by publisher's SomeEvent only Thread.Sleep(200); //We have waited enough,we don't require the Publisher now Publisher = null; GC.Collect(); //Even after GC.Collect,publisher is not collected even when we have set Publisher to null //This is because 'StartPublishing' method is under execution at this point of time //which means it is implicitly reachable from Main Thread's stack (through 'this' pointer) //This also means that subscriber remain alive //Even when we intended the Publisher to stop publishing,it will keep firing events due to somewhat 'hidden' reference to it from Main Thread!!!! } } internal class Publisher { public void StartPublishing() { Thread.Sleep(100); InvokeSomeEvent(null); Thread.Sleep(100); InvokeSomeEvent(null); Thread.Sleep(100); InvokeSomeEvent(null); Thread.Sleep(100); InvokeSomeEvent(null); } public event EventHandler SomeEvent; public void InvokeSomeEvent(object e) { EventHandler handler = SomeEvent; if (handler != null) { handler(this,null); } } ~Publisher() { Console.WriteLine("I am never Printed"); } } internal class Subscriber { public Subscriber() { if(MainClass.Publisher != null) { MainClass.Publisher.SomeEvent += PublisherSomeEvent; } } void PublisherSomeEvent(object sender,EventArgs e) { if (MainClass.Publisher == null) { //How can null fire an event!!! Raise Exception throw new Exception("Booooooooommmm"); //But notice 'sender' is not null } } } 如果你运行上面的代码,你通常会收到“Booooooooommmm”.所以想法是事件发布者必须停止触发事件,当我们确定它的生命已经到了. 这可以通过Dispose方法完成. 新200的X- 200 X- 200 200 X- 200 200: >设置一个标志’IsDisposed’,并在触发任何事件之前检查它. 2的好处是您释放对订户的任何引用,从而实现收集(正如我之前解释的,即使发布者是垃圾,但属于较高的一代,那么它仍然可能延长下一代用户的收集). 不过,诚然,由于发布商“隐藏”的可及性,您将体会到这种表现的行为,这是很罕见的,但是您可以看到2的优点是明确的,并且对于所有的活动发布商,特别是长寿的发行商(Singletons anybody! !).这本身使得值得实施Dispose并与2一起去. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |