订阅Singleton类发布的事件

时间:2013-06-06 10:31:05

标签: c# asp.net

我有一个名为CacheState的Singleton类。这个类发布了很多事件。 CacheState有一个System.Timers.Timer,它循环并触发所有这些事件。

然后在我的asp.net应用程序中,我在Application_Start期间订阅了这些事件。 CacheState中的Timer也在此期间启动:

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();

        }

我读过C#Events会导致内存泄漏。那么,任何人都可以告诉我,如果我做错了吗?

感谢。

3 个答案:

答案 0 :(得分:5)

单例实例包含对已订阅其事件的所有对象的引用。如果这些对象不像单例实例那样存在,则它们不会取消订阅这些事件,那么它们将保留在内存中。当您遇到内存泄漏时,这是唯一的情况。显然,如果事件源在您的侦听器之前被释放,则引用将被清除,如果您正确取消注册侦听器,则还没有引用。

要解决此问题,您可以实施Weak Event Pattern或实施例如所有听取单件事件的对象中的IDisposable都要确保它们在您的代码中得到妥善处理!

当然,不仅保持单例对象,而且对于任何充当事件源的对象。然而,单例事件源是一个特别危险的情况,因为它通常会在您的应用程序运行时存在,因此至少与任何其他对象一样长。

答案 1 :(得分:0)

如果您通过代码多次分配给事件,则C#会导致内存泄漏。 这可能发生在复杂的代码中,通常与初学者有关。 因此,您的eventhandler将被执行多次。

避免这种情况的一种正确方法是在附加事件(+ =)之前分离事件( - =),最好将其封装到管理该事件的方法中。

这确保指向事件处理方法的指针的事件堆栈只填充一个条目(如果需要)。

这是关于内存泄漏的一件事。

另一件事是参考。 如果您的事件处理程序访问全局对象,或全局对象列表并插入值,并且您没有跟踪,则事件处理程序将导致全局变量被实例化,使用,引用等。但这取决于设计。

如果有人知道更多,我会很感激。

答案 2 :(得分:0)

    // 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