Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/264.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在MVVM中使用dispose取消订阅事件_C#_Wpf_Mvvm - Fatal编程技术网

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方法时
  • 有没有更好的办法做这样的事
  • 但我知道我必须取消订阅我的活动,以避免内存泄漏

    当短寿命对象订阅了长寿命对象的事件(或静态事件)并且以后没有取消订阅(例如,请参阅答案)时,会发生内存泄漏。我想,这不是你的情况

    何时调用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;
         }
    }
    
    但我知道我必须取消订阅我的活动,以避免内存泄漏

    当短寿命对象订阅了长寿命对象的事件(或静态事件)并且以后没有取消订阅(例如,请参阅答案)时,会发生内存泄漏。我想,这不是你的情况

    何时调用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的路径