gpt4 book ai didi

c# - 使用 IObserver/IObservable 实现观察者和主题

转载 作者:行者123 更新时间:2023-11-30 19:19:56 36 4
gpt4 key购买 nike

我想创建一个可用于表示动态计算值的类,而另一个表示值的类可以是这些动态计算值的源(主题)。目标是当主题发生变化时,计算值会自动更新。

在我看来,使用 IObservable/IObserver 是可行的方法。不幸的是我不能使用 Reactive Extensions 库,所以我不得不从头开始实现主题/观察者模式。

废话不多说了,这是我的类(class):

public class Notifier<T> : IObservable<T>
{
public Notifier();
public IDisposable Subscribe(IObserver<T> observer);
public void Subscribe(Action<T> action);
public void Notify(T subject);
public void EndTransmission();
}

public class Observer<T> : IObserver<T>, IDisposable
{
public Observer(Action<T> action);
public void Subscribe(Notifier<T> tracker);
public void Unsubscribe();
public void OnCompleted();
public void OnError(Exception error);
public void OnNext(T value);
public void Dispose();
}

public class ObservableValue<T> : Notifier<T>
{
public T Get();
public void Set(T x);
}

public class ComputedValue<T>
{
public T Get();
public void Set(T x);
}

我的实现主要来自:http://msdn.microsoft.com/en-us/library/dd990377.aspx .

那么“正确”的方法是什么?注意:我不关心 LINQ 或多线程甚至性能。我只是希望它简单易懂。

最佳答案

如果我是你,我会尽量按照 Rx 的实现方式来实现你的类。

关键的基本原则之一是使用相对较少的具体类,这些类通过大量操作组合在一起。因此,您应该创建一些基本构建 block 并使用组合将它们组合在一起。

在 Reflector.NET 下我会初步了解两个类:AnonymousObservable<T> & AnonymousObserver<T> .特别是AnonymousObservable<T>在整个 Rx 中用作实例化可观察对象的基础。事实上,如果您查看派生自 IObservable<T> 的对象有一些专门的实现,但只有 AnonymousObservable<T>用于一般用途。

静态方法Observable.Create<T>()本质上是 AnonymousObservable<T> 的包装器.

另一个显然适合您要求的 Rx 类是 BehaviorSubject<T> .主题既是可观察者又是观察者并且BehaviorSubject适合您的情况,因为它会记住收到的最后一个值。

有了这些基本类,您几乎就拥有了创建特定对象所需的所有内容。您的对象不应继承上述代码,而应使用组合来组合您需要的行为。

现在,我建议对您的类设计进行一些更改,使它们与 Rx 更加兼容,从而更加可组合和健壮。

我会放弃你的 Notifier<T>类赞成使用 BehaviourSubject<T> .

我会放弃你的 Observer<T>类赞成使用 AnonymousObserver<T> .

然后我会修改ObservableValue<T>看起来像这样:

public class ObservableValue<T> : IObservable<T>, IDisposable
{
public ObservableValue(T initial) { ... }
public T Value { get; set; }
public IDisposable Subscribe(IObserver<T> observer);
public void Dispose();
}

执行ObservableValue<T>会包装 BehaviourSubject<T>而不是继承它来暴露 IObserver<T>成员将允许访问 OnCompleted & OnError这没有多大意义,因为此类代表一个值而不是计算。订阅将使用 AnonymousObservable<T>Dispose会清理包裹的 BehaviourSubject<T> .

然后我会修改ComputedValue<T>看起来像这样:

public class ComputedValue<T> : IObservable<T>, IDisposable
{
public ComputedValue(IObservable<T> source) { ... }
public T Value { get; }
public IDisposable Subscribe(IObserver<T> observer);
public void Dispose();
}

ComputedValue<T>类将包装 AnonymousObservable<T>所有订户并使用source获取 Value 值的本地副本属性(property)。 Dispose方法将用于取消订阅 source可观察。

这最后两个类是您的设计似乎需要的唯一真正的特定实现 - 这只是因为 Value属性(property)。

接下来你需要一个静态的ObservableValues扩展方法的类:

public static class ObservableValues
{
public static ObservableValue<T> Create<T>(T initial)
{ ... }

public static ComputedValue<V> Compute<T, U, V>(
this IObservable<T> left,
IObservable<U> right,
Func<T, U, V> computation)
{ ... }
}

Compute方法将使用 AnonymousObservable<V>执行计算并生成 IObservable<V>传递给 ComputedValue<V> 的构造函数由方法返回。

所有这些都准备就绪后,您现在可以编写以下代码:

var ov1 = ObservableValues.Create(1);
var ov2 = ObservableValues.Create(2);
var ov3 = ObservableValues.Create(3);

var cv1 = ov1.Compute(ov2, (x, y) => x + y);
var cv2 = ov3.Compute(cv1, (x, y) => x * y);

//cv2.Value == 9

ov1.Value = 2;
ov2.Value = 3;
ov3.Value = 4;

//cv2.Value == 20

请让我知道这是否有帮助和/或是否有任何我可以详细说明的内容。


编辑:还需要一些一次性用品。

您还需要实现 AnonymousDisposable & CompositeDisposable管理您的订阅,特别是在 Compute扩展方法。查看使用 Reflector.NET 的 Rx 实现或使用下面我的版本。

public sealed class AnonymousDisposable : IDisposable
{
private readonly Action _action;
private int _disposed;

public AnonymousDisposable(Action action)
{
_action = action;
}

public void Dispose()
{
if (Interlocked.Exchange(ref _disposed, 1) == 0)
{
_action();
}
}
}

public sealed class CompositeDisposable : IEnumerable<IDisposable>, IDisposable
{
private readonly List<IDisposable> _disposables;
private bool _disposed;

public CompositeDisposable()
: this(new IDisposable[] { })
{ }

public CompositeDisposable(IEnumerable<IDisposable> disposables)
{
if (disposables == null) { throw new ArgumentNullException("disposables"); }
this._disposables = new List<IDisposable>(disposables);
}

public CompositeDisposable(params IDisposable[] disposables)
{
if (disposables == null) { throw new ArgumentNullException("disposables"); }
this._disposables = new List<IDisposable>(disposables);
}

public void Add(IDisposable disposable)
{
if (disposable == null) { throw new ArgumentNullException("disposable"); }
lock (_disposables)
{
if (_disposed)
{
disposable.Dispose();
}
else
{
_disposables.Add(disposable);
}
}
}

public IDisposable Add(Action action)
{
if (action == null) { throw new ArgumentNullException("action"); }
var disposable = new AnonymousDisposable(action);
this.Add(disposable);
return disposable;
}

public IDisposable Add<TDelegate>(Action<TDelegate> add, Action<TDelegate> remove, TDelegate handler)
{
if (add == null) { throw new ArgumentNullException("add"); }
if (remove == null) { throw new ArgumentNullException("remove"); }
if (handler == null) { throw new ArgumentNullException("handler"); }
add(handler);
return this.Add(() => remove(handler));
}

public void Clear()
{
lock (_disposables)
{
var disposables = _disposables.ToArray();
_disposables.Clear();
Array.ForEach(disposables, d => d.Dispose());
}
}

public void Dispose()
{
lock (_disposables)
{
if (!_disposed)
{
this.Clear();
}
_disposed = true;
}
}

public IEnumerator<IDisposable> GetEnumerator()
{
lock (_disposables)
{
return _disposables.ToArray().AsEnumerable().GetEnumerator();
}
}

IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}

public bool IsDisposed
{
get
{
return _disposed;
}
}
}

关于c# - 使用 IObserver/IObservable 实现观察者和主题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7549688/

36 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com