Czy konieczne jest wyraźne usunięcie procedur obsługi zdarzeń w C#

Mam zajęcia, które oferują kilka wydarzeń. Ta klasa jest deklarowana globalnie, ale nie jest instancjonowana na tej globalnej deklaracji-jest instancjonowana w miarę potrzeb w metodach, które jej potrzebują.

Za każdym razem, gdy ta klasa jest potrzebna w metodzie, jest ona instancjonowana i procedury obsługi zdarzeń są rejestrowane. Czy konieczne jest wyraźne usunięcie procedur obsługi zdarzeń,zanim metoda wyjdzie poza zakres?

Gdy metoda wychodzi poza zakres, tak samo jak instancja klasy. Czy odejście procedury obsługi zdarzeń zarejestrowane w tej instancji, która jest poza zasięgiem, mają implikację footprintu pamięci? (Zastanawiam się, czy obsługa zdarzeń nie pozwala GC widzieć instancji klasy jako nieistniejącej.)

Dziękuję.

Author: Scott Dorman, 2009-02-03

2 answers

W Twoim przypadku wszystko jest w porządku. Jest to obiekt, który publikuje zdarzenia, które utrzymują cele procesów obsługi zdarzeń na żywo. Więc jeśli mam:

publisher.SomeEvent += target.DoSomething;

Wtedy publisher ma odniesienie do target, ale nie odwrotnie.

W Twoim przypadku wydawca będzie uprawniony do zbierania śmieci (zakładając, że nie ma do niego innych odniesień) , więc fakt, że ma odniesienie do celów obsługi zdarzeń, jest nieistotny.

The tricky przypadek jest, gdy wydawca jest długowieczny, ale subskrybenci nie chcą być-w , że przypadku trzeba zrezygnować z subskrypcji handlerów. Załóżmy na przykład, że masz usługę transferu danych, która pozwala subskrybować asynchroniczne powiadomienia o zmianach przepustowości, a obiekt usługi transferu jest długotrwały. Jeśli to zrobimy:

BandwidthUI ui = new BandwidthUI();
transferService.BandwidthChanged += ui.HandleBandwidthChange;
// Suppose this blocks until the transfer is complete
transferService.Transfer(source, destination);
// We now have to unsusbcribe from the event
transferService.BandwidthChanged -= ui.HandleBandwidthChange;

(w rzeczywistości chcesz użyć bloku finally, aby upewnić się, że nie wyciekasz obsługi zdarzenia. Jeśli nie wypisaliśmy się z subskrypcji, to BandwidthUI będzie żył co najmniej tak długo, jak usługa transferu.

Osobiście rzadko się na to natykam-zazwyczaj jeśli subskrybuję wydarzenie, cel tego wydarzenia żyje co najmniej tak długo, jak wydawca-formularz będzie trwał tak długo, jak przycisk, który jest na nim, na przykład. Warto wiedzieć o tym potencjalnym problemie, ale myślę, że niektórzy martwią się o to, kiedy nie muszą, ponieważ nie wiedzą, w którą stronę idą odniesienia.

EDIT: [29] Komentarz Dickinsona. Po pierwsze, spójrz na dokumenty dla delegata.Equals (object) , które wyraźnie dają zachowanie równości.

Po Drugie, oto krótki, ale kompletny program pokazujący działanie wypisywania:

using System;

public class Publisher
{
    public event EventHandler Foo;

    public void RaiseFoo()
    {
        Console.WriteLine("Raising Foo");
        EventHandler handler = Foo;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
        else
        {
            Console.WriteLine("No handlers");
        }
    }
}

public class Subscriber
{
    public void FooHandler(object sender, EventArgs e)
    {
        Console.WriteLine("Subscriber.FooHandler()");
    }
}

public class Test
{
    static void Main()
    {
         Publisher publisher = new Publisher();
         Subscriber subscriber = new Subscriber();
         publisher.Foo += subscriber.FooHandler;
         publisher.RaiseFoo();
         publisher.Foo -= subscriber.FooHandler;
         publisher.RaiseFoo();
    }
}

Wyniki:

Raising Foo
Subscriber.FooHandler()
Raising Foo
No handlers

(testowane na Mono i. NET 3. 5SP1.)

Dalsza edycja:

Ma to na celu udowodnienie, że wydawca zdarzenia może być pobrany, gdy nadal istnieją odniesienia do subskrybenta.

using System;

public class Publisher
{
    ~Publisher()
    {
        Console.WriteLine("~Publisher");
        Console.WriteLine("Foo==null ? {0}", Foo == null);
    }

    public event EventHandler Foo;
}

public class Subscriber
{
    ~Subscriber()
    {
        Console.WriteLine("~Subscriber");
    }

    public void FooHandler(object sender, EventArgs e) {}
}

public class Test
{
    static void Main()
    {
         Publisher publisher = new Publisher();
         Subscriber subscriber = new Subscriber();
         publisher.Foo += subscriber.FooHandler;

         Console.WriteLine("No more refs to publisher, "
             + "but subscriber is alive");
         GC.Collect();
         GC.WaitForPendingFinalizers();         

         Console.WriteLine("End of Main method. Subscriber is about to "
             + "become eligible for collection");
         GC.KeepAlive(subscriber);
    }
}

Wyniki (w. NET 3. 5SP1; Mono wydaje się tu zachowywać nieco dziwnie. Przyjrzę się temu kiedyś):

No more refs to publisher, but subscriber is alive
~Publisher
Foo==null ? False
End of Main method. Subscriber is about to become eligible for collection
~Subscriber
 167
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-12 14:24:35

W Twoim przypadku nic Ci nie jest. Pierwotnie przeczytałem twoje pytanie od tyłu, że abonent wychodzi poza zakres, a nie wydawca . Jeśli wydawca zdarzenia wyjdzie poza zakres, to odsyła do subskrybenta (oczywiście nie samego subskrybenta!) go with it and there is no need to explicite remove them.

Moja oryginalna odpowiedź jest poniżej, o tym, co się stanie, jeśli utworzysz Zdarzenie subskrybent i pozwolisz mu wyjść poza zakres bez rezygnacja z subskrypcji. To nie dotyczy twojego pytania, ale zostawię je na miejscu dla historii.

Jeśli klasa jest nadal rejestrowana przez procedury obsługi zdarzeń, to nadal jest osiągalna. To wciąż żywy obiekt. GC po wykresie zdarzeń znajdzie je połączone. Tak, będziesz chciał wyraźnie usunąć procedury obsługi zdarzeń.

To, że obiekt jest poza zakresem jego pierwotnego przydziału, nie oznacza, że jest kandydatem do GC. Tak długo, jak żywe odniesienie pozostaje, jest żyj.

 7
Author: Eddie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-03 14:23:10