// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics;
using System.Globalization;
using System.Collections.Generic;
using System.Reactive.Concurrency;
#pragma warning disable 0659
#pragma warning disable 0661
namespace System.Reactive
{
///
/// Indicates the type of a notification.
///
public enum NotificationKind
{
///
/// Represents an OnNext notification.
///
OnNext,
///
/// Represents an OnError notification.
///
OnError,
///
/// Represents an OnCompleted notification.
///
OnCompleted
}
///
/// Represents a notification to an observer.
///
/// The type of the elements received by the observer.
#if !NO_SERIALIZABLE
[Serializable]
#endif
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals", Justification = "Resembles a discriminated union with finite number of subclasses (external users shouldn't create their own subtypes), each of which does override GetHashCode itself.")]
public abstract class Notification : IEquatable>
{
///
/// Default constructor used by derived types.
///
protected internal Notification()
{
}
///
/// Returns the value of an OnNext notification or throws an exception.
///
public abstract T Value
{
get;
}
///
/// Returns a value that indicates whether the notification has a value.
///
public abstract bool HasValue
{
get;
}
///
/// Returns the exception of an OnError notification or returns null.
///
public abstract Exception Exception
{
get;
}
///
/// Gets the kind of notification that is represented.
///
public abstract NotificationKind Kind
{
get;
}
///
/// Represents an OnNext notification to an observer.
///
#if !NO_DEBUGGER_ATTRIBUTES
[DebuggerDisplay("OnNext({Value})")]
#endif
#if !NO_SERIALIZABLE
[Serializable]
#endif
internal sealed class OnNextNotification : Notification
{
T value;
///
/// Constructs a notification of a new value.
///
public OnNextNotification(T value)
{
this.value = value;
}
///
/// Returns the value of an OnNext notification.
///
public override T Value { get { return value; } }
///
/// Returns null.
///
public override Exception Exception { get { return null; } }
///
/// Returns true.
///
public override bool HasValue { get { return true; } }
///
/// Returns NotificationKind.OnNext.
///
public override NotificationKind Kind { get { return NotificationKind.OnNext; } }
///
/// Returns the hash code for this instance.
///
public override int GetHashCode()
{
return EqualityComparer.Default.GetHashCode(Value);
}
///
/// Indicates whether this instance and a specified object are equal.
///
public override bool Equals(Notification other)
{
if (Object.ReferenceEquals(this, other))
return true;
if (Object.ReferenceEquals(other, null))
return false;
if (other.Kind != NotificationKind.OnNext)
return false;
return EqualityComparer.Default.Equals(Value, other.Value);
}
///
/// Returns a string representation of this instance.
///
public override string ToString()
{
return String.Format(CultureInfo.CurrentCulture, "OnNext({0})", Value);
}
///
/// Invokes the observer's method corresponding to the notification.
///
/// Observer to invoke the notification on.
public override void Accept(IObserver observer)
{
if (observer == null)
throw new ArgumentNullException("observer");
observer.OnNext(Value);
}
///
/// Invokes the observer's method corresponding to the notification and returns the produced result.
///
/// Observer to invoke the notification on.
/// Result produced by the observation.
public override TResult Accept(IObserver observer)
{
if (observer == null)
throw new ArgumentNullException("observer");
return observer.OnNext(Value);
}
///
/// Invokes the delegate corresponding to the notification.
///
/// Delegate to invoke for an OnNext notification.
/// Delegate to invoke for an OnError notification.
/// Delegate to invoke for an OnCompleted notification.
public override void Accept(Action onNext, Action onError, Action onCompleted)
{
if (onNext == null)
throw new ArgumentNullException("onNext");
if (onError == null)
throw new ArgumentNullException("onError");
if (onCompleted == null)
throw new ArgumentNullException("onCompleted");
onNext(Value);
}
///
/// Invokes the delegate corresponding to the notification and returns the produced result.
///
/// Delegate to invoke for an OnNext notification.
/// Delegate to invoke for an OnError notification.
/// Delegate to invoke for an OnCompleted notification.
/// Result produced by the observation.
public override TResult Accept(Func onNext, Func onError, Func onCompleted)
{
if (onNext == null)
throw new ArgumentNullException("onNext");
if (onError == null)
throw new ArgumentNullException("onError");
if (onCompleted == null)
throw new ArgumentNullException("onCompleted");
return onNext(Value);
}
}
///
/// Represents an OnError notification to an observer.
///
#if !NO_DEBUGGER_ATTRIBUTES
[DebuggerDisplay("OnError({Exception})")]
#endif
#if !NO_SERIALIZABLE
[Serializable]
#endif
internal sealed class OnErrorNotification : Notification
{
Exception exception;
///
/// Constructs a notification of an exception.
///
public OnErrorNotification(Exception exception)
{
this.exception = exception;
}
///
/// Throws the exception.
///
public override T Value { get { exception.Throw(); return default(T); } }
///
/// Returns the exception.
///
public override Exception Exception { get { return exception; } }
///
/// Returns false.
///
public override bool HasValue { get { return false; } }
///
/// Returns NotificationKind.OnError.
///
public override NotificationKind Kind { get { return NotificationKind.OnError; } }
///
/// Returns the hash code for this instance.
///
public override int GetHashCode()
{
return Exception.GetHashCode();
}
///
/// Indicates whether this instance and other are equal.
///
public override bool Equals(Notification other)
{
if (Object.ReferenceEquals(this, other))
return true;
if (Object.ReferenceEquals(other, null))
return false;
if (other.Kind != NotificationKind.OnError)
return false;
return Object.Equals(Exception, other.Exception);
}
///
/// Returns a string representation of this instance.
///
public override string ToString()
{
return String.Format(CultureInfo.CurrentCulture, "OnError({0})", Exception.GetType().FullName);
}
///
/// Invokes the observer's method corresponding to the notification.
///
/// Observer to invoke the notification on.
public override void Accept(IObserver observer)
{
if (observer == null)
throw new ArgumentNullException("observer");
observer.OnError(Exception);
}
///
/// Invokes the observer's method corresponding to the notification and returns the produced result.
///
/// Observer to invoke the notification on.
/// Result produced by the observation.
public override TResult Accept(IObserver observer)
{
if (observer == null)
throw new ArgumentNullException("observer");
return observer.OnError(Exception);
}
///
/// Invokes the delegate corresponding to the notification.
///
/// Delegate to invoke for an OnNext notification.
/// Delegate to invoke for an OnError notification.
/// Delegate to invoke for an OnCompleted notification.
public override void Accept(Action onNext, Action onError, Action onCompleted)
{
if (onNext == null)
throw new ArgumentNullException("onNext");
if (onError == null)
throw new ArgumentNullException("onError");
if (onCompleted == null)
throw new ArgumentNullException("onCompleted");
onError(Exception);
}
///
/// Invokes the delegate corresponding to the notification and returns the produced result.
///
/// Delegate to invoke for an OnNext notification.
/// Delegate to invoke for an OnError notification.
/// Delegate to invoke for an OnCompleted notification.
/// Result produced by the observation.
public override TResult Accept(Func onNext, Func onError, Func onCompleted)
{
if (onNext == null)
throw new ArgumentNullException("onNext");
if (onError == null)
throw new ArgumentNullException("onError");
if (onCompleted == null)
throw new ArgumentNullException("onCompleted");
return onError(Exception);
}
}
///
/// Represents an OnCompleted notification to an observer.
///
#if !NO_DEBUGGER_ATTRIBUTES
[DebuggerDisplay("OnCompleted()")]
#endif
#if !NO_SERIALIZABLE
[Serializable]
#endif
internal sealed class OnCompletedNotification : Notification
{
///
/// Constructs a notification of the end of a sequence.
///
public OnCompletedNotification()
{
}
///
/// Throws an InvalidOperationException.
///
public override T Value { get { throw new InvalidOperationException(Strings_Core.COMPLETED_NO_VALUE); } }
///
/// Returns null.
///
public override Exception Exception { get { return null; } }
///
/// Returns false.
///
public override bool HasValue { get { return false; } }
///
/// Returns NotificationKind.OnCompleted.
///
public override NotificationKind Kind { get { return NotificationKind.OnCompleted; } }
///
/// Returns the hash code for this instance.
///
public override int GetHashCode()
{
return typeof(T).GetHashCode() ^ 8510;
}
///
/// Indicates whether this instance and other are equal.
///
public override bool Equals(Notification other)
{
if (Object.ReferenceEquals(this, other))
return true;
if (Object.ReferenceEquals(other, null))
return false;
return other.Kind == NotificationKind.OnCompleted;
}
///
/// Returns a string representation of this instance.
///
public override string ToString()
{
return "OnCompleted()";
}
///
/// Invokes the observer's method corresponding to the notification.
///
/// Observer to invoke the notification on.
public override void Accept(IObserver observer)
{
if (observer == null)
throw new ArgumentNullException("observer");
observer.OnCompleted();
}
///
/// Invokes the observer's method corresponding to the notification and returns the produced result.
///
/// Observer to invoke the notification on.
/// Result produced by the observation.
public override TResult Accept(IObserver observer)
{
if (observer == null)
throw new ArgumentNullException("observer");
return observer.OnCompleted();
}
///
/// Invokes the delegate corresponding to the notification.
///
/// Delegate to invoke for an OnNext notification.
/// Delegate to invoke for an OnError notification.
/// Delegate to invoke for an OnCompleted notification.
public override void Accept(Action onNext, Action onError, Action onCompleted)
{
if (onNext == null)
throw new ArgumentNullException("onNext");
if (onError == null)
throw new ArgumentNullException("onError");
if (onCompleted == null)
throw new ArgumentNullException("onCompleted");
onCompleted();
}
///
/// Invokes the delegate corresponding to the notification and returns the produced result.
///
/// Delegate to invoke for an OnNext notification.
/// Delegate to invoke for an OnError notification.
/// Delegate to invoke for an OnCompleted notification.
/// Result produced by the observation.
public override TResult Accept(Func onNext, Func onError, Func onCompleted)
{
if (onNext == null)
throw new ArgumentNullException("onNext");
if (onError == null)
throw new ArgumentNullException("onError");
if (onCompleted == null)
throw new ArgumentNullException("onCompleted");
return onCompleted();
}
}
///
/// Determines whether the current Notification<T> object has the same observer message payload as a specified Notification<T> value.
///
/// An object to compare to the current Notification<T> object.
/// true if both Notification<T> objects have the same observer message payload; otherwise, false.
///
/// Equality of Notification<T> objects is based on the equality of the observer message payload they represent, including the notification Kind and the Value or Exception (if any).
/// This means two Notification<T> objects can be equal even though they don't represent the same observer method call, but have the same Kind and have equal parameters passed to the observer method.
/// In case one wants to determine whether two Notification<T> objects represent the same observer method call, use Object.ReferenceEquals identity equality instead.
///
public abstract bool Equals(Notification other);
///
/// Determines whether the two specified Notification<T> objects have the same observer message payload.
///
/// The first Notification<T> to compare, or null.
/// The second Notification<T> to compare, or null.
/// true if the first Notification<T> value has the same observer message payload as the second Notification<T> value; otherwise, false.
///
/// Equality of Notification<T> objects is based on the equality of the observer message payload they represent, including the notification Kind and the Value or Exception (if any).
/// This means two Notification<T> objects can be equal even though they don't represent the same observer method call, but have the same Kind and have equal parameters passed to the observer method.
/// In case one wants to determine whether two Notification<T> objects represent the same observer method call, use Object.ReferenceEquals identity equality instead.
///
public static bool operator ==(Notification left, Notification right)
{
if (object.ReferenceEquals(left, right))
return true;
if ((object)left == null || (object)right == null)
return false;
return left.Equals(right);
}
///
/// Determines whether the two specified Notification<T> objects have a different observer message payload.
///
/// The first Notification<T> to compare, or null.
/// The second Notification<T> to compare, or null.
/// true if the first Notification<T> value has a different observer message payload as the second Notification<T> value; otherwise, false.
///
/// Equality of Notification<T> objects is based on the equality of the observer message payload they represent, including the notification Kind and the Value or Exception (if any).
/// This means two Notification<T> objects can be equal even though they don't represent the same observer method call, but have the same Kind and have equal parameters passed to the observer method.
/// In case one wants to determine whether two Notification<T> objects represent a different observer method call, use Object.ReferenceEquals identity equality instead.
///
public static bool operator !=(Notification left, Notification right)
{
return !(left == right);
}
///
/// Determines whether the specified System.Object is equal to the current Notification<T>.
///
/// The System.Object to compare with the current Notification<T>.
/// true if the specified System.Object is equal to the current Notification<T>; otherwise, false.
///
/// Equality of Notification<T> objects is based on the equality of the observer message payload they represent, including the notification Kind and the Value or Exception (if any).
/// This means two Notification<T> objects can be equal even though they don't represent the same observer method call, but have the same Kind and have equal parameters passed to the observer method.
/// In case one wants to determine whether two Notification<T> objects represent the same observer method call, use Object.ReferenceEquals identity equality instead.
///
public override bool Equals(object obj)
{
return Equals(obj as Notification);
}
///
/// Invokes the observer's method corresponding to the notification.
///
/// Observer to invoke the notification on.
public abstract void Accept(IObserver observer);
///
/// Invokes the observer's method corresponding to the notification and returns the produced result.
///
/// The type of the result returned from the observer's notification handlers.
/// Observer to invoke the notification on.
/// Result produced by the observation.
public abstract TResult Accept(IObserver observer);
///
/// Invokes the delegate corresponding to the notification.
///
/// Delegate to invoke for an OnNext notification.
/// Delegate to invoke for an OnError notification.
/// Delegate to invoke for an OnCompleted notification.
public abstract void Accept(Action onNext, Action onError, Action onCompleted);
///
/// Invokes the delegate corresponding to the notification and returns the produced result.
///
/// The type of the result returned from the notification handler delegates.
/// Delegate to invoke for an OnNext notification.
/// Delegate to invoke for an OnError notification.
/// Delegate to invoke for an OnCompleted notification.
/// Result produced by the observation.
public abstract TResult Accept(Func onNext, Func onError, Func onCompleted);
///
/// Returns an observable sequence with a single notification, using the immediate scheduler.
///
/// The observable sequence that surfaces the behavior of the notification upon subscription.
public IObservable ToObservable()
{
return this.ToObservable(ImmediateScheduler.Instance);
}
///
/// Returns an observable sequence with a single notification.
///
/// Scheduler to send out the notification calls on.
/// The observable sequence that surfaces the behavior of the notification upon subscription.
public IObservable ToObservable(IScheduler scheduler)
{
if (scheduler == null)
throw new ArgumentNullException("scheduler");
return new AnonymousObservable(observer => scheduler.Schedule(() =>
{
this.Accept(observer);
if (this.Kind == NotificationKind.OnNext)
observer.OnCompleted();
}));
}
}
///
/// Provides a set of static methods for constructing notifications.
///
public static class Notification
{
///
/// Creates an object that represents an OnNext notification to an observer.
///
/// The type of the elements received by the observer. Upon dematerialization of the notifications into an observable sequence, this type is used as the element type for the sequence.
/// The value contained in the notification.
/// The OnNext notification containing the value.
public static Notification CreateOnNext(T value)
{
return new Notification.OnNextNotification(value);
}
///
/// Creates an object that represents an OnError notification to an observer.
///
/// The type of the elements received by the observer. Upon dematerialization of the notifications into an observable sequence, this type is used as the element type for the sequence.
/// The exception contained in the notification.
/// The OnError notification containing the exception.
/// is null.
public static Notification CreateOnError(Exception error)
{
if (error == null)
throw new ArgumentNullException("error");
return new Notification.OnErrorNotification(error);
}
///
/// Creates an object that represents an OnCompleted notification to an observer.
///
/// The type of the elements received by the observer. Upon dematerialization of the notifications into an observable sequence, this type is used as the element type for the sequence.
/// The OnCompleted notification.
public static Notification CreateOnCompleted()
{
return new Notification.OnCompletedNotification();
}
}
}
#pragma warning restore 0659
#pragma warning restore 0661