简体   繁体   中英

Subscribing to events published by a Singleton Class

I have a Singleton class named CacheState. This class publishes many events. The CacheState has a System.Timers.Timer that loops and triggers all these events.

Then in my asp.net application, I subscribe to these events during Application_Start. The Timer in CacheState is also started during this time:

protected void Application_Start(object sender, EventArgs e)
        {           
                CacheState.Instance.ProductChangedEvent += (objSender, argsE) => ProductService.ReloadProductCache(false);
                CacheState.Instance.PageflexChangedEvent += (objSender, argsE) => ProductService.ResetPageflexCache(false);
                CacheState.Instance.DeliveryChangedEvent += (objSender, argsE) => PricingRuleService.ResetDeliveryMethodsCache(false);
                CacheState.Instance.UIItemChangedEvent += (objSender, argsE) => TemplateService.ResetUIItemsCache(false);
                CacheState.Instance.ProductAttributeChangedEvent += Instance_ProductAttributeChangedEvent;
                CacheState.Instance.Start();

        }

I've read that C# Events can cause memory leaks. So, can anyone tell me if I'm doing this wrong?

Thanks.

The singleton instance holds a references to all objects that have subscribed to its events. If those objects do not live as long as the singleton instance and they do not unsubscribe from these events then they will remain in memory. This is the only case when you experience a memory leak. Clearly, if the event source gets disposed before your listeners the reference will get cleared and if you properly unregister your listeners there is also no reference remaining.

To solve this problem you can implement the Weak Event Pattern or implement eg IDisposable in all objects that listen to singleton events and make sure they get properly disposed in your code!

Of course this does not only hold for singleton objects, but for any object that acts as an event source. A singleton event source however is a particularly dangerous case as it usually lives as long as your application runs and thus lives at least as long as any other object.

C# can cause memory leaks, if You assign to an event via code more than once. This can happen in complex codes and often with beginners. And due to this Your eventhandler will be executed more than once.

A proper way to avoid this, is to detach the event ( -= ) before attaching it (+= ), best was to be encapsulated into a method , which manages that.

This ensures that the event stack of pointers to Your event handling method is filled with exactly one entry, ( if so desired ).

That is one thing I know about memory leaks.

Another thing is references. If Your eventhandler accesses a global object, or a list of global objects and inserts values, and You do not keep track, the eventhandler will cause a global variable to become instantiated, used, referenced and so on. But this depends on design.

If anybody does know more, i would appreciate this.

    // generic delegate for genric events
public delegate void EventsHandler<in TArgs>(TArgs args) where TArgs : EventArgs;

// generic singleton
public abstract class EventsBase<TEvents> where TEvents : class, new()
{
    private static readonly object lockObject = new object();

    private static volatile TEvents instance;

    public static TEvents Instance
    {
        get
        {
            if (instance == null)
            {
                lock (lockObject)
                {
                    if (instance == null)
                    {
                        instance = new TEvents();
                    }
                }
            }
            return instance;
        }
    }
}

public class EventArgs<T> : EventArgs
{
    public T Item { get; set; }

    public EventArgs(T item)
    {
        Item = item;
    }
}

public class MyEvents : EventsBase<MyEvents>
{

    public event EventsHandler<EventArgs<IList<int>>> OnCheckedDataBase;
    public event EventsHandler<EventArgs<IList<int>>> OnProcessedData;

    public void CheckedDataBase(IList<int> handler)
    {
        if (OnCheckedDataBase != null)
        {
            OnCheckedDataBase(new EventArgs<IList<int>>(handler));
        }
    }
    public void ProcessedData(IList<int> handler)
    {
        if (OnProcessedData != null)
        {
            OnProcessedData(new EventArgs<IList<int>>(handler));
        }
    }

 }

MyEvents.Instance.OnCheckedDataBase += OnCheckedDataBase; //register

MyEvents.Instance.CheckedDataBase(this);  //fire

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM