C# 在MVVM中使用dispose取消订阅事件
实际上,我正试图通过从我的C# 在MVVM中使用dispose取消订阅事件,c#,wpf,mvvm,C#,Wpf,Mvvm,实际上,我正试图通过从我的ViewModel触发事件来关闭窗口。一切都很好,令人敬畏,但我知道我必须取消订阅我的活动,以避免内存泄漏。因此,我实现了IDisposable接口,并在Dispose方法中取消订阅事件 下面是我的代码: public partial class MainWindow : Window, IDisposable { private MainViewModel viewModel; public MainWindow() { Ini
ViewModel
触发事件来关闭窗口。一切都很好,令人敬畏,但我知道我必须取消订阅我的活动,以避免内存泄漏。因此,我实现了IDisposable
接口,并在Dispose
方法中取消订阅事件
下面是我的代码:
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方法时MainWindow.Dispose
,它将永远不会被调用
有没有更好的办法做这样的事
我会避免IDisposable和事件。依附行为在这里更方便,IMO(至少,这是可重用的):
在window的XAML中的某个位置:
behaviors:WindowClosingBehavior.IsClosingInitiated="{Binding IsClosingInitiated}"
其中,isclosininitiated
是视图模型中的属性:
public class SomeViewModel
{
// ...
private void Foo()
{
// ...
IsClosingInitiated = true;
}
}
但我知道我必须取消订阅我的活动,以避免内存泄漏
当短寿命对象订阅了长寿命对象的事件(或静态事件)并且以后没有取消订阅(例如,请参阅答案)时,会发生内存泄漏。我想,这不是你的情况
何时调用GC并执行dispose方法
GC不调用IDisposable.Dispose(例如,请参阅答案)。完全
如果您没有任何代码,则显式调用MainWindow.Dispose
,它将永远不会被调用
有没有更好的办法做这样的事
我会避免IDisposable和事件。依附行为在这里更方便,IMO(至少,这是可重用的):
在window的XAML中的某个位置:
behaviors:WindowClosingBehavior.IsClosingInitiated="{Binding IsClosingInitiated}"
其中,isclosininitiated
是视图模型中的属性:
public class SomeViewModel
{
// ...
private void Foo()
{
// ...
IsClosingInitiated = true;
}
}
实际上,当
Window.CloseWindow
订阅事件时,它会使视图模型指向该窗口
反之亦然,因为窗口中有一个ViewModel
字段
窗口模型和视图模型都相互引用
如果没有其他对它们的引用,则垃圾收集将使作业成功
如果某些代码调用Dispose,则将调用它
据我所知,这不会发生,除非您在创建窗口时使用或显式调用Dispose
这里最好的方法是不要实现IDisposable
/Dispose
:保持简单
实际上,当窗口.CloseWindow订阅事件时,它使视图模型指向该窗口
反之亦然,因为窗口中有一个ViewModel
字段
窗口模型和视图模型都相互引用
如果没有其他对它们的引用,则垃圾收集将使作业成功
如果某些代码调用Dispose,则将调用它
据我所知,这不会发生,除非您在创建窗口时使用或显式调用Dispose
这里最好的方法是不要实现IDisposable
/Dispose
:保持简单
关于您只需要在源和处理程序具有不同生存期时取消订阅事件,否则它们都会同时超出范围,并一起被垃圾收集
因此,在这种情况下,不需要IDisposable。无论如何,如果您实现IDisposable,您需要显式调用它,否则您无法控制何时调用它。您只需要在源和处理程序具有不同生存期时取消订阅事件,否则它们都会同时超出范围,并一起被垃圾收集
因此,在这种情况下,不需要IDisposable。无论如何,如果您实现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所指出的,您需要保留终结器,以确保在关闭主窗口时调用Dispose
,例如在退出应用程序的情况下。我认为使用事件是实现这一点的一种完全可以接受的方法。有关更完整的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所指出的,您需要保留终结器,以确保在关闭主窗口时调用Dispose
,例如在退出应用程序的情况下。在此设置中,您根本不需要取消订阅事件-事件将保留一个指向正确窗口的指针,但是当没有到窗口的路径时,也不会有到viewmodel的路径,因此当窗口作为一个平视窗口时,它将被收集;您可能需要阅读一些适当的IDispose
实现。(,)@Carsten您能解释一下吗?在这种情况下,我们必须实现dispose方法尝试使用附加行为,事实上我不太喜欢事件,尤其是在MVVM中。在这种设置中,您根本不需要取消订阅事件-事件将保留指向正确窗口的指针,但是,如果没有指向窗口的路径,则也不会有指向viewmodel的路径