// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Reactive.Disposables;
using System.Reactive.Linq;
namespace System.Reactive.Subjects
{
///
/// Represents an observable wrapper that can be connected and disconnected from its underlying observable sequence.
///
/// The type of the elements in the source sequence.
/// The type of the elements in the resulting sequence, after transformation through the subject.
internal class ConnectableObservable : IConnectableObservable
{
private readonly ISubject _subject;
private readonly IObservable _source;
private readonly object _gate;
private Connection _connection;
///
/// Creates an observable that can be connected and disconnected from its source.
///
/// Underlying observable source sequence that can be connected and disconnected from the wrapper.
/// Subject exposed by the connectable observable, receiving data from the underlying source sequence upon connection.
public ConnectableObservable(IObservable source, ISubject subject)
{
_subject = subject;
_source = source.AsObservable(); // This gets us auto-detach behavior; otherwise, we'd have to roll our own, including trampoline installation.
_gate = new object();
}
///
/// Connects the observable wrapper to its source. All subscribed observers will receive values from the underlying observable sequence as long as the connection is established.
///
/// Disposable object used to disconnect the observable wrapper from its source, causing subscribed observer to stop receiving values from the underlying observable sequence.
public IDisposable Connect()
{
lock (_gate)
{
if (_connection == null)
{
var subscription = _source.SubscribeSafe(_subject);
_connection = new Connection(this, subscription);
}
return _connection;
}
}
class Connection : IDisposable
{
private readonly ConnectableObservable _parent;
private IDisposable _subscription;
public Connection(ConnectableObservable parent, IDisposable subscription)
{
_parent = parent;
_subscription = subscription;
}
public void Dispose()
{
lock (_parent._gate)
{
if (_subscription != null)
{
_subscription.Dispose();
_subscription = null;
_parent._connection = null;
}
}
}
}
///
/// Subscribes an observer to the observable sequence. No values from the underlying observable source will be received unless a connection was established through the Connect method.
///
/// Observer that will receive values from the underlying observable source when the current ConnectableObservable instance is connected through a call to Connect.
/// Disposable used to unsubscribe from the observable sequence.
public IDisposable Subscribe(IObserver observer)
{
if (observer == null)
throw new ArgumentNullException("observer");
return _subject.SubscribeSafe(observer);
}
}
}