好的,伙计们,我认为至少值得一试,并且看到 RX 的 Subject 不是我想要的,我创建了一个新的 observable 适合我的需求:
- 实现 IObservable
- 实现 INotifyPropertyChange 以使用 WPF/Silverlight 绑定。
- 提供简单的 get/set 语义。
我称这个类为 Observable。
声明:
/// <summary>
/// Represents a value whose changes can be observed.
/// </summary>
/// <typeparam name="T">The type of value.</typeparam>
public class Observable<T> : IObservable<T>, INotifyPropertyChanged
{
private T value;
private readonly List<AnonymousObserver> observers = new List<AnonymousObserver>(2);
private PropertyChangedEventHandler propertyChanged;
/// <summary>
/// Constructs a new observable with a default value.
/// </summary>
public Observable()
{
}
public Observable(T initalValue)
{
this.value = initialValue;
}
/// <summary>
/// Gets the underlying value of the observable.
/// </summary>
public T Value
{
get { return this.value; }
set
{
var valueHasChanged = !EqualityComparer<T>.Default.Equals(this.value, value);
this.value = value;
// Notify the observers of the value.
this.observers
.Select(o => o.Observer)
.Where(o => o != null)
.Do(o => o.OnNext(value))
.Run();
// For INotifyPropertyChange support, useful in WPF and Silverlight.
if (valueHasChanged && propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs("Value"));
}
}
}
/// <summary>
/// Converts the observable to a string. If the value isn't null, this will return
/// the value string.
/// </summary>
/// <returns>The value .ToString'd, or the default string value of the observable class.</returns>
public override string ToString()
{
return value != null ? value.ToString() : "Observable<" + typeof(T).Name + "> with null value.";
}
/// <summary>
/// Implicitly converts an Observable to its underlying value.
/// </summary>
/// <param name="input">The observable.</param>
/// <returns>The observable's value.</returns>
public static implicit operator T(Observable<T> input)
{
return input.Value;
}
/// <summary>
/// Subscribes to changes in the observable.
/// </summary>
/// <param name="observer">The subscriber.</param>
/// <returns>A disposable object. When disposed, the observer will stop receiving events.</returns>
public IDisposable Subscribe(IObserver<T> observer)
{
var disposableObserver = new AnonymousObserver(observer);
this.observers.Add(disposableObserver);
return disposableObserver;
}
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
add { this.propertyChanged += value; }
remove { this.propertyChanged -= value; }
}
class AnonymousObserver : IDisposable
{
public IObserver<T> Observer { get; private set; }
public AnonymousObserver(IObserver<T> observer)
{
this.Observer = observer;
}
public void Dispose()
{
this.Observer = null;
}
}
}
用法:
消费很简单。没有管道!
public class Foo
{
public Foo()
{
Progress = new Observable<T>();
}
public Observable<T> Progress { get; private set; }
}
用法很简单:
// Getting the value works just like normal, thanks to implicit conversion.
int someValue = foo.Progress;
// Setting the value is easy, too:
foo.Progress.Value = 42;
您可以在 WPF 或 Silverlight 中对其进行数据绑定,只需绑定到 Value 属性即可。
<ProgressBar Value={Binding Progress.Value} />
最重要的是,您可以使用 IObservables 编写、过滤、投影和做所有 RX 允许您做的性感事情:
过滤事件:
foo.Progress
.Where(val => val == 100)
.Subscribe(_ => MyProgressFinishedHandler());
N 次调用后自动取消订阅:
foo.Progress
.Take(1)
.Subscribe(_ => OnProgressChangedOnce());
编写事件:
// Pretend we have an IObservable<bool> called IsClosed:
foo.Progress
.TakeUntil(IsClosed.Where(v => v == true))
.Subscribe(_ => ProgressChangedWithWindowOpened());
好东西!