我想知道是否可以为Rx IObservable
设置一些全能。
它的行为如下:“如果没有其他订阅者观察到此消息,则执行[某事]”。
现在我连接了几个彼此不了解的Observable处理程序,并根据某些属性过滤事件。如果我们收到一条未被处理的消息,我想抛出一个错误,因为它将是一条无效的消息。
答案 0 :(得分:4)
我认为这是一个有趣的问题,所以我去写了一个中等大小的解决方案。我已将解决方案分为三个部分:通用实现,示例用法和解释。
public interface ITracked<out T>
{
T Value { get; }
bool IsObserved { get; }
void Observe();
}
public class Tracked<T> : ITracked<T>
{
private readonly T value;
public Tracked(T value)
{
this.value = value;
}
public T Value
{
get { return value; }
}
public bool IsObserved { get; private set; }
public void Observe()
{
IsObserved = true;
}
}
public interface ITrackableObservable<out T> : IObservable<ITracked<T>>
{
IObservable<T> Unobserved { get; }
}
public class TrackableObservable<T> : ITrackableObservable<T>
{
private readonly ISubject<T> unobserved = new Subject<T>();
private readonly IObservable<ITracked<T>> source;
public TrackableObservable(IObservable<T> source)
{
this.source = Observable
.Create<ITracked<T>>(observer => source.Subscribe(
value =>
{
var trackedValue = new Tracked<T>(value);
observer.OnNext(trackedValue);
if (!trackedValue.IsObserved)
{
unobserved.OnNext(value);
}
},
observer.OnError,
observer.OnCompleted))
.Publish()
.RefCount();
}
public IObservable<T> Unobserved
{
get { return unobserved.AsObservable(); }
}
public IDisposable Subscribe(IObserver<ITracked<T>> observer)
{
return source.Subscribe(observer);
}
}
public static class TrackableObservableExtensions
{
public static ITrackableObservable<T> ToTrackableObservable<T>(this IObservable<T> source)
{
return new TrackableObservable<T>(source);
}
public static IObservable<T> Observe<T>(this IObservable<ITracked<T>> source)
{
return source.Do(x => x.Observe()).Select(x => x.Value);
}
public static IObservable<T> ObserveWhere<T>(this IObservable<ITracked<T>> source, Func<T, bool> predicate)
{
return source.Where(x => predicate(x.Value)).Observe();
}
}
public class Animal
{
public int ID { get; set; }
public string Kind { get; set; }
public string Name { get; set; }
}
...
IObservable<Animal> animals = ...;
ITrackableObservable<Animal> trackableAnimals = animals.ToTrackableObservable();
trackableAnimals
.ObserveWhere(a => a.Kind == "Cat")
.Subscribe(a => Console.WriteLine("{0}: Meow", a.ID));
trackableAnimals
.ObserveWhere(a => a.Kind == "Dog")
.Subscribe(a => Console.WriteLine("{0}: Woof", a.ID));
trackableAnimals
.ObserveWhere(a => a.Name != null)
.Subscribe(a => Console.WriteLine("{0}: {1} named {2}", a.ID, a.Kind, a.Name));
trackableAnimals
.Unobserved
.Subscribe(a => Console.WriteLine("{0}: {1} with no name (unobserved)", a.ID, a.Kind));
如果animals
要发出此序列:
new Animal { ID = 1, Kind = "Cat", Name = "Rusty" }
new Animal { ID = 2, Kind = "Horse" }
new Animal { ID = 3, Kind = "Dog", Name = "Fido" }
new Animal { ID = 4, Kind = "Dog" }
new Animal { ID = 5, Kind = "Bird", Name = "Simon" }
然后我们会看到这个输出:
1: Meow
1: Cat named Rusty
2: Horse with no name (unobserved)
3: Woof
3: Dog named Fido
4: Woof
5: Bird named Simon
这里的想法是确保所有订阅者最终共享对源序列的单个订阅,并且每个值都附加bool
,表示是否已观察到该值。每当源序列发出T
时,我们将其包装为ITracked<T>
,然后将该单个实例传递给所有订阅者。然后观察者可以选择将值标记为观察值。一旦对观察者的所有OnNext
次调用都返回,如果ITracked<T>
未标记为已观察,则我们知道它未被观察到。
TrackableObservableExtensions
类提供了一些扩展方法,使得实现更流畅,但实现过程中并不需要它们。