我有3个关于事件的问题:
我有这样的代码:
Ctor:目的:用于数据库属性更新
this.PropertyChanged += (o, e) =>
{
switch (e.PropertyName)
{
case "FirstName": break;
case "LastName": break;
}
};
和this:目的:用于GUI绑定将模型包装到viewmodels
ObservableCollection<Period> periods = _lpRepo.GetDailyLessonPlanner(data.DailyDate);
PeriodListViewModel = new ObservableCollection<PeriodViewModel>();
foreach (Period period in periods)
{
PeriodViewModel periodViewModel = new PeriodViewModel(period,_lpRepo);
foreach (DocumentListViewModel documentListViewModel in periodViewModel.DocumentViewModelList)
{
documentListViewModel.DeleteDocumentDelegate += new Action<List<Document>>(OnDeleteDocument);
documentListViewModel.AddDocumentDelegate += new Action(OnAddDocument);
documentListViewModel.OpenDocumentDelegate += new Action<int, string>(OnOpenDocument);
}
PeriodListViewModel.Add(periodViewModel);
}
答案 0 :(得分:52)
好吧,让我们先回答最后一个问题。您无法可靠地取消订阅您直接使用lambda表达式订阅的事件。你要么需要保持变量周围的委托(所以你仍然可以使用lambda表达式)或你需要使用方法组转换。
现在至于你是否真的需要取消订阅,这取决于事件生产者和事件消费者之间的关系。如果事件生成器的活动时间比事件使用者长,那么应取消订阅 - 否则生产者将引用消费者,使其保持活动的时间超过应有的时间。只要生产者生成它,事件处理程序也将继续被调用。
现在在许多情况下这不是问题 - 例如,在一个表单中,引发Click
事件的按钮可能存在的时间与创建它的表单一样长,其中处理程序通常是订阅...所以没有必要取消订阅。这对于GUI来说非常典型。
同样,如果您仅为单个异步请求创建WebClient
,请订阅相关事件并启动异步请求,那么WebClient
本身将有资格进行垃圾回收。请求已经完成(假设您没有在别处保留引用)。
基本上,您应该始终考虑生产者和消费者之间的关系。如果生产者的寿命超过了你希望消费者的时间,或它会在你不再对它感兴趣之后继续提升事件,那么你应该取消订阅。
答案 1 :(得分:27)
1)这取决于。通常这是一个好主意,但有一些典型的情况你不需要。基本上,如果您确定订阅对象将比事件源更长,那么您应该取消订阅,否则会产生不必要的引用。
但是,如果您的对象订阅了自己的事件,请执行以下操作:
<Window Loaded="self_Loaded" ...>...</Window>
- 然后你不必。
2)订阅事件会对订阅对象进行额外引用。因此,如果您不取消订阅,您的对象可能会通过此引用保持活动,从而有效地进行内存泄漏。通过取消订阅,您将删除该引用。请注意,在自我订阅的情况下,不会出现问题。
3)你可以这样做:
this.PropertyChanged += PropertyChangedHandler;
...
this.PropertyChanged -= PropertyChangedHandler;
,其中
void PropertyChangedHandler(object o, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "FirstName": break;
case "LastName": break;
}
}
答案 2 :(得分:4)
您可以在MSDN上查看this article。引用:
阻止您的事件处理程序 在事件发生时被调用 提出,只是取消订阅 事件。为了防止资源 泄漏,取消订阅很重要 处理之前的事件 订户对象。直到你 取消订阅活动, 作为基础的多播委托 发布对象中的事件有一个 参考代表那个 封装订户的事件 处理程序。只要出版 对象持有该引用,您的 订阅者对象不会是垃圾 收集。
答案 3 :(得分:4)
当订阅的实例与正在订阅的实例具有相同的范围时,您不必从事件中取消订阅。
让我们说你是一个表格,你正在订阅一个控件,这两个一起形成一个组。但是,如果您有一个管理表单的中心类,并且您已订阅该表单的Closed
事件,则这些事件不会组成一个组,并且您必须在表单关闭后进行取消订阅。
订阅事件会使订阅的实例创建对正在订阅的实例的引用。这可以防止垃圾回收。因此,当您有一个管理表单实例的中心类时,这将使所有表单保留在内存中。
WPF是一个例外,因为它有一个弱事件模型,其中事件使用弱引用进行订阅,并且它不会将表单保存在内存中。但是,当您不是表格的一部分时,最佳做法是取消订阅。
答案 4 :(得分:1)
1。)我是否应该总是取消订阅订阅的活动?
通常是的。唯一的例外是当你订阅的对象不再被引用时,很快就会被垃圾收集。
2。)如果我不这样做会怎么样?
您订阅的对象将保存对委托的引用,该委托又保留对其this
指针的引用,因此您将获得内存泄漏。
或者如果处理程序是lamda,它将保留它绑定的任何局部变量,因此也不会被收集。