在MVVM中使用dispose取消订阅事件
本文关键字:取消 事件 dispose MVVM | 更新日期: 2023-09-27 18:21:17
实际上,我正试图通过从ViewModel
启动事件来关闭我的窗口。一切都很好,很棒,但我知道我必须取消订阅我的活动,以避免内存泄露。因此我实现了CCD_ 2接口,并且在CCD_。
以下是我的代码:
public partial class MainWindow : Window, IDisposable
{
private MainViewModel viewModel;
public MainWindow()
{
InitializeComponent();
DataContext = viewModel = new MainViewModel();
this.viewModel.RequestClose += CloseWindow;
}
void CloseWindow(object sender, EventArgs e)
{
this.Close();
}
public void Dispose()
{
////here we need to unsubscribe the event
this.viewModel.RequestClose -= this.CloseWindow;
}
}
我需要知道的是:
- 那个代码正确吗
- 何时调用
GC
并执行dispose方法 - 做这样的事有更好的方法吗
但我知道我必须取消订阅我的活动以避免内存泄漏
当短命对象订阅了一个长命对象的事件(或静态事件),并且以后没有取消订阅时,就会发生内存泄漏(例如,请参阅此答案)。我想,这不是你的情况。
何时调用GC并执行处置方法
GC不调用IDisposable.Dispose
(例如,请参阅此答案)。完全如果您没有任何明确调用MainWindow.Dispose
的代码,它将永远不会被调用。
有没有更好的方法来做这样的事情
我会避开IDisposable
和活动。这里的附加行为更方便,IMO(至少,这是可重复使用的):
public static class WindowClosingBehavior
{
public static bool GetIsClosingInitiated(DependencyObject obj)
{
return (bool)obj.GetValue(IsClosingInitiatedProperty);
}
public static void SetIsClosingInitiated(DependencyObject obj, bool value)
{
obj.SetValue(IsClosingInitiatedProperty, value);
}
public static readonly DependencyProperty IsClosingInitiatedProperty = DependencyProperty.RegisterAttached(
"IsClosingInitiated",
typeof(bool),
typeof(WindowClosingBehavior),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsClosingInitiatedChanged));
private static void IsClosingInitiatedChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
var window = target as Window;
if (window != null && (bool)e.NewValue)
{
window.Close();
}
}
}
窗口XAML中的某个位置:
behaviors:WindowClosingBehavior.IsClosingInitiated="{Binding IsClosingInitiated}"
其中IsClosingInitiated
是来自视图模型的属性:
public class SomeViewModel
{
// ...
private void Foo()
{
// ...
IsClosingInitiated = true;
}
}
只有当源和处理程序的生存期不同时,才需要取消订阅事件,否则它们同时超出范围,并被垃圾收集在一起。
因此,在这种情况下,不需要IDisposable。无论如何,如果您实现IDisposable,您需要显式地调用它,否则您无法控制何时调用它。
实际上,当Window.CloseWindow
订阅事件时,它会使视图模型指向窗口。
反之亦然,因为在Window
中存在ViewModel
字段。
窗口和视图模型都相互参照。
如果没有其他引用,垃圾回收将完成任务。
如果某些代码调用Dispose
,则会调用它。
据我所知,除非用using
或显式调用Dispose
来创建窗口,否则这种情况不会发生
这里最好的方法是不实现IDisposable
/Dispose
:保持简单。
问候
我认为使用事件是一种完全可以接受的方法。要获得更完整的处置模式,请使用以下代码段:
#region IDisposable
//Dispose() calls Dispose(true)
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// NOTE: Delete the finalizer if this class doesn't
// own unmanaged resources itself.
~ClassName()
{
//Finalizer calls Dispose(false)
Dispose(false);
}
//The bulk of the clean-up code is implemented in Dispose(bool)
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
//free managed resources (Example below)
if (managedResource != null)
{
managedResource.Dispose();
managedResource = null;
}
}
//Free native resources if there are any. (Example below)
if (nativeResource != IntPtr.Zero)
{
Marshal.FreeHGlobal(nativeResource);
nativeResource = IntPtr.Zero;
}
}
#endregion
在您的情况下,您的处理方法是:
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~MainWindow()
{
Dispose();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (viewModel != null)
{
viewModel.RequestClose -= CloseWindow;
viewModel.Dispose();
viewModel = null;
}
}
}
正如Dennis所指出的,您需要保留终结器,以确保在MainWindow
关闭时(例如在应用程序退出的情况下)调用Dispose
。