我很喜歡使用IDisposable
這種事情。事實上,使用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()
被調用你的類,它會自動被運行一次且僅一次的集合。
這對於事件處理程序來說特別好,因爲它允許您將旁邊的添加處理程序代碼放在源代碼中去除處理程序代碼,這使得重構變得更加簡單。如果代碼在添加處理程序旁邊,實際上是否刪除處理程序非常簡單。
要做到這一點,你需要在代碼中添加兩個類。
首先是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
用於打開一個Action
成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使用方法級變量,所以這個選項可以幫助保持你的模塊得到所有混亂。現在
,你可以停在這一點,但你可能已經注意到在CompositeDisposable
類有助於增加事件訂閱,像這樣的另一個Add
超載:
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) =>
{
// ...
});
甜,是吧?
我希望這會有所幫助。
我沒有受過良好的MVVM教育,但我的第一個想法:*是'DetailViewModel:IDisposable' ... * ???如果是這樣...在Dispose()方法中取消訂閱。 – IAbstract
@IAbstract我認爲'IDisposable'被垃圾收集器用於已經收集的物品;例如在沒有其他對該對象的引用存在時關閉打開的文件或數據庫集合。但是,這將阻止首先收集對象,所以'Dispose()'永遠不會被調用。我誤解'IDisposable'? – mbmcavoy
我從未讀過任何你無法讀到的東西。此外,我認爲這是終結者,**實際**解除引用對象,或釋放GC ...?我認爲... – IAbstract