What I want to do is ensure that if the only reference to my observer is the observable, it get\'s garbage collected and stops receiving messages.
Say I have a control w
The code below is inspired by dtb's original post. The only change is that it returns a reference to the observer as part of the IDisposable. This means that the reference to the IObserver will be kept alive as long as you keep a reference to the IDisposable that you get out at the end of the chain (assuming all disposables keep a reference to the disposable before them). This allows the usage of the extension methods such as Subscribe(M=>DoSomethingWithM(M))
because we keep a reference to the implicitly constructed IObserver but we don't keep a strong reference from the source to the IObserver (which would produce a memory leek).
using System.Reactive.Linq;
static class WeakObservation
{
public static IObservable ToWeakObservable(this IObservable observable)
{
return Observable.Create(observer =>
(IDisposable)new DisposableReference(new WeakObserver(observable, observer), observer)
);
}
}
class DisposableReference : IDisposable
{
public DisposableReference(IDisposable InnerDisposable, object Reference)
{
this.InnerDisposable = InnerDisposable;
this.Reference = Reference;
}
private IDisposable InnerDisposable;
private object Reference;
public void Dispose()
{
InnerDisposable.Dispose();
Reference = null;
}
}
class WeakObserver : IObserver, IDisposable
{
private readonly WeakReference reference;
private readonly IDisposable subscription;
private bool disposed;
public WeakObserver(IObservable observable, IObserver observer)
{
this.reference = new WeakReference(observer);
this.subscription = observable.Subscribe(this);
}
public void OnCompleted()
{
var observer = (IObserver)this.reference.Target;
if (observer != null) observer.OnCompleted();
else this.Dispose();
}
public void OnError(Exception error)
{
var observer = (IObserver)this.reference.Target;
if (observer != null) observer.OnError(error);
else this.Dispose();
}
public void OnNext(T value)
{
var observer = (IObserver)this.reference.Target;
if (observer != null) observer.OnNext(value);
else this.Dispose();
}
public void Dispose()
{
if (!this.disposed)
{
this.disposed = true;
this.subscription.Dispose();
}
}
}