c# – .NET对象事件和处理/ GC

前端之家收集整理的这篇文章主要介绍了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不是这么说的

However,there is an issue with events such that sometimes people like to use IDisposable with types that have events. The problem is that when a type X subscribes to events in another type Y,X now has a reference to Y. This reference will prevent Y from being collected.

我不明白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();
    }    
}

b1,and I know they will now
subscribed to prevIoUs instance of _a,and it’s OK by me,BUT in that
example,nobody references the prevIoUs instance of _a (b not
holds reference to _a) and by my theory,now
may be collected…or I’m missing something???

正如我在上述代码中的评论所解释的,你没有在这里遗漏任何东西:)

BUT…as I know,it’s not guaranteed when GC will collect the
publisher so in theory,even if subscribers lifetime is greater then
publishers,it can happen that subscriber is legal for collection,but
publisher is still not collected (I don’t know if within closest GC
cycle,GC will be smart enough to collect publisher first and then
subscriber.

由于发布商引用订阅者,所以在发布商之前,订阅者无法收到资格,但是倒序可能是真实的.如果发布者在订阅者之前收集,正如你所说,没有问题.如果订阅者属于比发布商更低的GC代码,则由于发布者持有对订阅者的引用,GC将把订阅者视为可达到并且不会收集.如果两者都属于同一代人,他们将被收集在一起.

since my subscriber do not have direct reference to publisher and
can’t unsubscribe the event,I would like to make publisher to
implement IDisposable

与某些建议相反,如果您确定确定不再需要该对象,我建议您执行处理.简单地更新对象引用可能并不总是导致对象停止发布事件.

请考虑以下代码

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一起去.

猜你在找的C#相关文章