我有一个名为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会导致内存泄漏。那么,任何人都可以告诉我,如果我做错了吗?
感谢。
答案 0 :(得分:5)
单例实例包含对已订阅其事件的所有对象的引用。如果这些对象不像单例实例和那样存在,则它们不会取消订阅这些事件,那么它们将保留在内存中。当您遇到内存泄漏时,这是唯一的情况。显然,如果事件源在您的侦听器之前被释放,则引用将被清除,如果您正确取消注册侦听器,则还没有引用。
要解决此问题,您可以实施Weak Event Pattern或实施例如所有听取单件事件和的对象中的IDisposable
都要确保它们在您的代码中得到妥善处理!
当然,不仅保持单例对象,而且对于任何充当事件源的对象。然而,单例事件源是一个特别危险的情况,因为它通常会在您的应用程序运行时存在,因此至少与任何其他对象一样长。
答案 1 :(得分:0)
避免这种情况的一种正确方法是在附加事件(+ =)之前分离事件( - =),最好将其封装到管理该事件的方法中。
这确保指向事件处理方法的指针的事件堆栈只填充一个条目(如果需要)。
这是关于内存泄漏的一件事。
另一件事是参考。 如果您的事件处理程序访问全局对象,或全局对象列表并插入值,并且您没有跟踪,则事件处理程序将导致全局变量被实例化,使用,引用等。但这取决于设计。
如果有人知道更多,我会很感激。
答案 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