Optymalizacja klas z wieloma zdarzeniami

0

Mam pytanie odnośnie optymalizacji klas z wieloma zdarzeniami. Załóżmy, że klasa ma 20 zdarzeń, a jak wiadomo nie zawsze ze wszystkich się korzysta. Stąd też niby powinno napisać się kod służący do optymalizacji zdarzeń, czyli utworzyć kolekcję zawierającą używane zdarzenia i napisać właściwości dostępu do tych zdarzeń przykładowy kod poniżej
(źródło: https://msdn.microsoft.com/pl-pl/library/optymalizacja-kodu-c-sharp--czesc-2.aspx)

class SampleClass
{
    protected EventHandlerList _events = new EventHandlerList();

    public event EventHandler SampleEvent1
    {
        add { _events.AddHandler("SampleEvent1", value); }
        remove { _events.RemoveHandler("SampleEvent1", value); }
    }
    
    public void TestEvent1()
    {
        EventHandler eh = _events["SampleEvent1"] as EventHandler;
        if (eh != null)
        {
            eh(this, null);
        }
    }
}

Dzięki temu kompilator nie utworzy dla każdego zdarzenia pola zawierającego delegate. No i wszystko byłoby ok gdyby nie fakt, że jeżeli będziemy mieli dla jednego zdarzenia sporo subskrybentów to za każdym razem gdy dodawana będzie metoda obsługi to będzie tworzony obiekt typu string ("SampleEvent1")będący kluczem umożliwiającym odnalezienie odpowiedniego delegata w kolekcji. Dodatkowo przy każdym wywołaniu zdarzenia również musi zostać utworzony obiekt typu string. Więc pytanie brzmi gdzie tutaj ta optymalizacja? Użytkownik może wywoływać zdarzenie milion razy więc wtedy zostanie utworzonych milion obiektów typu string, a tak utworzonych zostałoby tylko 20 typu EventHandler. No chyba, że jest coś o czym nie wiem, że jednak ta optymalizacja wnosi coś dobrego dlatego pytam.

0

Chyba dlatego właśnie w artykule napisano, żeby jako klucz używać obiektu w statycznym polu readonly.

3

Nie będzie tworzony za każdym razem nowy obiekt typu string, tylko wykorzystany już wcześniej utworzony. Te obiektu typu string znane na etapie kompilacji nazywa się literałami, a one są inaczej obsługiwane niż stringi tworzone w trakcie działania programu i jest wykorzystywana tylko jedna ich instancja przez cały czas działania programu:

Strange string literal comparison

0

Czyli jedna rzecz wyjaśniona. Ten przykład ze stringami zaczął mnie zastanawiać, bo zasugerowałem się wcześniejszym tekstem, gdzie autor napisał, że dla literałów za każdym razem jest tworzony nowy obiekt. W sensie, że dla takiego kodu

		string text = "a";
        for (int i = 0; i < 10000; i++)
        text += "test" + i.ToString();

dla każdej iteracji są tworzone 4 obiekty typu string: jeden dla "test", jeden dla i.ToString(), jeden dla ich złączenia i jeden dla złączenia text z wynikiem po prawej stronie.

Ale teraz mam jeszcze jedno pytanie, bo jeżeli nie napiszemy odpowiedniego kodu optymalizującego no to kompilator utworzy nam dla każdego zdarzenia pole EventHandler, a zawartość będzie nullem? Na to by wychodziło, no więc dlaczego podane przykłady mają dokonać jakiejś optymalizacji skoro dla każdego zdarzenia musimy utworzyć obiekt będący kluczem umożliwiającym odszukanie tego zdarzenia w kolekcji (czy to string czy zwykły object). Dodatkowo stracimy troszkę czasu na przeszukanie tej kolekcji, gdy będziemy chcieli wywołać te zdarzenie. Z góry dziękuję za odpowiedź.

2

Zapewne chodzi o dokonanie trade offu w tym wypadku oszczędzenie pamięci kosztem mocy obliczeniowej.
Zauważ że ten obiekt będacy kluczem jest wspólny dla wszystkich instancji, czyli static readonly(singleton).

Czyli dla klasy mającej 100 eventów i n instancji mamy pod względem pamięci:
EventHandler - 100 * rozmiar eventu * n
EventHandlerList - n * rozmiar listy + 100 * rozmiar klucza

rozmiar listy równa sie jakieś małej stałej plus liczbie podpiętych eventów pod daną instancje.
W więksozści wypadków ta druga liczba będzie znacznie mniejsza.

1 użytkowników online, w tym zalogowanych: 0, gości: 1