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?
}
Run Code Online (Sandbox Code Playgroud)
据我所知,事件处理程序将使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)
{ /* ... */ }
}
Run Code Online (Sandbox Code Playgroud)
这可以让你做的是将各种清理代码粘贴到一个集合中,该集合自动运行一次,只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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
而第二个 - 用于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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
该类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);
Run Code Online (Sandbox Code Playgroud)
您可以在lamdbas中使用方法级变量,因此该选项可以帮助您保持模块的混乱.
现在,您可以停止此操作,但您可能已经注意到该类中的另一个Add重载CompositeDisposable有助于添加事件订阅,如下所示:
PropertyChangedEventHandler handler = (s, e) => { /* ... */ };
_disposables.Add(
h => model.PropertyChanged += h,
h => model.PropertyChanged -= h,
handler);
Run Code Online (Sandbox Code Playgroud)
这完成了从处理程序订阅和取消订阅的整个工作.
您甚至可以更进一步,在一行中完成所有操作,如下所示:
_disposables.Add<PropertyChangedEventHandler>(
h => model.PropertyChanged += h,
h => model.PropertyChanged -= h,
(s, e) =>
{
// ...
});
Run Code Online (Sandbox Code Playgroud)
甜,对吧?
我希望这有帮助.
起初我认为这是要走的路:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
但后来我发现了这个美丽的金块.因此,至少有两种可能的解决方案:(a)实施样本IDisposable,以及(b)反对的论据IDisposable.我会把辩论留给你.;)
你也可以考虑WeakEvent模式 ......
| 归档时间: |
|
| 查看次数: |
14782 次 |
| 最近记录: |