mbm*_*voy 18 .net c# events garbage-collection mvvm
在我的应用程序中,我经常创建新的视图和ViewModel,但持久保存相同的模型.例如,我可能会在主窗口中显示项目列表的简单视图,并具有另一个窗口,其中包含任何特定项目的更多详细信息.可以随时打开和关闭详细信息窗口,也可以同时打开列表中不同项目的多个窗口.
因此,给定模型对象可以有多个ViewModel,并且需要使用其他位置的更改进行更新.(我正在使用INotifyPropertyChanged我的模型.)当我完成它时,我想摆脱ViewModels,即,当详细信息窗口关闭时.
public DetailViewModel(MyDetailModel detailModel)
{
    // Retain the Detail Model
    this.model = detailModel;
    // Handle changes to the Model not coming from this ViewModel
    this.model.PropertyChanged += model_PropertyChanged;  // Potential leak?
}
据我所知,事件处理程序将使Model保留对ViewModel的引用,并防止它被垃圾收集.
1)这是正确的吗?如何判断这些引用是否仍然存在?
2)我应该如何确定不再需要ViewModel并取消订阅事件?
Eni*_*ity 10
我非常喜欢使用IDisposable这种东西.事实上,您可以使用a CompositeDisposable来获得出色的结果来满足您的所有清理需求.
这是我做的:
public class DetailViewModel : IDisposable
{
    private readonly CompositeDisposable _disposables
        = new CompositeDisposable();
    public void Dispose()
    {
        _disposables.Dispose();
    }
    private readonly MyDetailModel _model;
    public DetailViewModel(MyDetailModel model)
    {
        _model = model;
        _model.PropertyChanged += _model_PropertyChanged;
        Action removeHandler = () =>
            _model.PropertyChanged -= _model_PropertyChanged;
        _disposables.Add(removeHandler);
    }
    private void _model_PropertyChanged(
        object sender, PropertyChangedEventArgs e)
    { /* ... */ }
}
这可以让你做的是将各种清理代码粘贴到一个集合中,该集合自动运行一次,只IDisposable.Dispose()在你的类上调用一次.
这对于事件处理程序特别好,因为它允许您在源代码中删除处理程序代码旁边添加处理程序代码,这使得重构更加简单.如果代码在add处理程序旁边,那么很容易看出你是否真的删除了处理程序.
要实现这一点,您需要在代码中添加两个类.
首先是CompositeDisposable:
public sealed class CompositeDisposable : IEnumerable<IDisposable>, IDisposable
{
    private readonly List<IDisposable> _disposables;
    private bool _disposed;
    public CompositeDisposable()
    {
        _disposables = new List<IDisposable>();
    }
    public CompositeDisposable(IEnumerable<IDisposable> disposables)
    {
        if (disposables == null)
            { throw new ArgumentNullException("disposables"); }
        _disposables = new List<IDisposable>(disposables);
    }
    public CompositeDisposable(params IDisposable[] disposables)
    {
        if (disposables == null)
            { throw new ArgumentNullException("disposables"); }
        _disposables = new List<IDisposable>(disposables);
    }
    public void Add(IDisposable disposable)
    {
        if (disposable == null)
            { throw new ArgumentNullException("disposable"); }
        lock (_disposables)
        {
            if (_disposed)
            {
                disposable.Dispose();
            }
            else
            {
                _disposables.Add(disposable);
            }
        }
    }
    public IDisposable Add(Action action)
    {
        if (action == null) { throw new ArgumentNullException("action"); }
        var disposable = new AnonymousDisposable(action);
        this.Add(disposable);
        return disposable;
    }
    public IDisposable Add<TDelegate>(
            Action<TDelegate> add,
            Action<TDelegate> remove,
            TDelegate handler)
    {
        if (add == null) { throw new ArgumentNullException("add"); }
        if (remove == null) { throw new ArgumentNullException("remove"); }
        if (handler == null) { throw new ArgumentNullException("handler"); }
        add(handler);
        return this.Add(() => remove(handler));
    }
    public void Clear()
    {
        lock (_disposables)
        {
            var disposables = _disposables.ToArray();
            _disposables.Clear();
            Array.ForEach(disposables, d => d.Dispose());
        }
    }
    public void Dispose()
    {
        lock (_disposables)
        {
            if (!_disposed)
            {
                this.Clear();
            }
            _disposed = true;
        }
    }
    public IEnumerator<IDisposable> GetEnumerator()
    {
        lock (_disposables)
        {
            return _disposables.ToArray().AsEnumerable().GetEnumerator();
        }
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
    public bool IsDisposed
    {
        get
        {
            return _disposed;
        }
    }
}
而第二个 - 用于CompositeDisposable- 是AnonymousDisposable.
public sealed class AnonymousDisposable : IDisposable
{
    private readonly Action _action;
    private int _disposed;
    public AnonymousDisposable(Action action)
    {
        _action = action;
    }
    public void Dispose()
    {
        if (Interlocked.Exchange(ref _disposed, 1) == 0)
        {
            _action();
        }
    }
}
该类AnonymousDisposable用于将a Action转换为a,IDisposable以便在处理时运行该操作AnonymousDisposable.
您现在可以轻松使用的另一个选项是使用匿名事件处理程序,而不是需要定义私有方法来处理事件.
您可以在构造函数中使用它:
        PropertyChangedEventHandler handler = (s, e) =>
        {
            // Use inline lambdas instead of private methods to handle events
        };
        model.PropertyChanged += handler;
        _disposables.Add(() => model.PropertyChanged -= handler);
您可以在lamdbas中使用方法级变量,因此该选项可以帮助您保持模块的混乱.
现在,您可以停止此操作,但您可能已经注意到该类中的另一个Add重载CompositeDisposable有助于添加事件订阅,如下所示:
        PropertyChangedEventHandler handler = (s, e) => { /* ... */ };
        _disposables.Add(
                    h => model.PropertyChanged += h,
                    h => model.PropertyChanged -= h,
                    handler);
这完成了从处理程序订阅和取消订阅的整个工作.
您甚至可以更进一步,在一行中完成所有操作,如下所示:
        _disposables.Add<PropertyChangedEventHandler>(
            h => model.PropertyChanged += h,
            h => model.PropertyChanged -= h,
            (s, e) =>
                {
                    // ...
                });
甜,对吧?
我希望这有帮助.
起初我认为这是要走的路:
public class DetailViewModel : IDisposable
{
    public DetailViewModel(MyDetailModel detailModel)
    {
        // Retain the Detail Model
        this.model = detailModel;
        // Handle changes to the Model not coming from this ViewModel
        this.model.PropertyChanged += model_PropertyChanged;  // Potential leak?
    }
    public void Dispose()
    {
        this.model.PropertyChanged -= model_PropertyChanged;
    }
}
但后来我发现了这个美丽的金块.因此,至少有两种可能的解决方案:(a)实施样本IDisposable,以及(b)反对的论据IDisposable.我会把辩论留给你.;)
你也可以考虑WeakEvent模式 ......