Однако, хотя eventStream все еще существует и должен храниться в памяти, с помощью этого метода было создано 2 новых IObservable - одна, возвращаемая методом Where (), которая предположительно хранится в памяти с помощью eventStream, и одна возвращается методом Cast (), который предположительно хранится в памяти тем, который возвращается методом Where ().
У вас есть это назад. Давайте пройдемся по цепочке происходящего.
IObservable<T> eventStream; //you have this defined and assigned somewhere
public IDisposable Subscribe<T>(IObserver<T> observer)
{
//let's break this method into multiple lines
IObservable<T> whereObs = eventStream.Where(e => e is T);
//whereObs now has a reference to eventStream (and thus will keep it alive),
//but eventStream knows nothing of whereObs (thus whereObs will not be kept alive by eventStream)
IObservable<T> castObs = whereObs.Cast<T>();
//as with whereObs, castObs has a reference to whereObs,
//but no one has a reference to castObs
IDisposable ret = castObs.Subscribe(observer);
//here is where it gets tricky.
return ret;
}
То, на что ret
ссылается или не имеет ссылки, зависит от реализации различных наблюдаемых. Из того, что я видел в Reflector в библиотеке Rx и в операторах, которые я написал сам, большинство операторов не возвращают расходные материалы, имеющие ссылку на сам наблюдаемый оператор.
Например, базовая реализация Where
будет выглядеть примерно так (набирается непосредственно в редакторе, без обработки ошибок)
IObservable<T> Where<T>(this IObservable<T> source, Func<T, bool> filter)
{
return Observable.Create<T>(obs =>
{
return source.Subscribe(v => if (filter(v)) obs.OnNext(v),
obs.OnError, obs.OnCompleted);
}
}
Обратите внимание, что возвращаемое одноразовое использование будет иметь ссылку на функцию фильтра через созданного наблюдателя, но не будет иметь ссылку на наблюдаемое Where
. Cast
может быть легко реализовано с использованием того же шаблона. По сути, операторы становятся фабриками-наблюдателями.
Смысл всего этого для рассматриваемого вопроса заключается в том, что промежуточные IObservables могут собираться мусором к концу метода . Функция фильтра, переданная в Where
, остается неизменной в течение всего срока действия подписки, но после удаления или завершения подписки остается только eventStream
(при условии, что она еще жива).
РЕДАКТИРОВАТЬ для комментария суперкатера, давайте посмотрим, как компилятор мог бы переписать это или как бы вы реализовали это без замыканий.
class WhereObserver<T> : IObserver<T>
{
WhereObserver<T>(IObserver<T> base, Func<T, bool> filter)
{
_base = base;
_filter = filter;
}
IObserver<T> _base;
Func<T, bool> _filter;
void OnNext(T value)
{
if (filter(value)) _base.OnNext(value);
}
void OnError(Exception ex) { _base.OnError(ex); }
void OnCompleted() { _base.OnCompleted(); }
}
class WhereObservable<T> : IObservable<T>
{
WhereObservable<T>(IObservable<T> source, Func<T, bool> filter)
{
_source = source;
_filter = filter;
}
IObservable<T> source;
Func<T, bool> filter;
IDisposable Subscribe(IObserver<T> observer)
{
return source.Subscribe(new WhereObserver<T>(observer, filter));
}
}
static IObservable<T> Where(this IObservable<T> source, Func<T, bool> filter)
{
return new WhereObservable(source, filter);
}
Вы можете видеть, что наблюдателю не нужна ссылка на наблюдаемую, которая его сгенерировала, и наблюдаемой не нужно отслеживать наблюдателей, которых он создает. Мы даже не сделали никаких новых идентификаторов для возврата из нашей подписки.
В действительности Rx имеет несколько реальных классов для анонимных наблюдаемых / наблюдателей, которые принимают делегатов и перенаправляют вызовы интерфейса этим делегатам. Он использует замыкания для создания этих делегатов. Компилятору не нужно выдавать классы, которые фактически реализуют интерфейсы, но дух перевода остается прежним.