EventHandler 与 Rx Subject
EventHandler vs Rx Subject
我正在使用 System.Reactive
,但我不知道该选择哪一个:EventHandlers 或 Subjects。它们有什么区别?
var client = WebSocketClient.Create(uri);
// Subject
client.OnConnected
.Subscribe(_ => { Log.Information($"Socket {client.Id} connected"); })
.DisposeWith(disposable);
// EventHandler
Observable
.FromEventPattern(h => client.Connected += h, h => client.Connected -= h)
.Select(_ => Unit.Default)
.Subscribe(_ => { Log.Information($"Socket {client.Id} connected"); })
.DisposeWith(disposable);
public class WebSocketClient : IWebSocketClient
{
// Subject
private readonly ISubject<Unit> _connectedSubject = new Subject<Unit>();
public IObservable<Unit> OnConnected => _connectedSubject.AsObservable();
// EventHandler
private EventHandler? _connected;
public event EventHandler Connected
{
add => _connected += value;
remove => _connected -= value;
}
// Logic
async Task IWebSocketClient.ConnectAsync(CancellationToken cancellationToken)
{
...
await _webSocket.ConnectAsync(_uri, cancellationToken).ConfigureAwait(false);
_connected?.Invoke(this, EventArgs.Empty);
_connectedSubject.OnNext();
...
}
private void Dispose()
{
_connectedSubject.OnCompleted();
}
}
下面是一段代码示例,它更好地说明了使用主题和事件来创建可观察对象。
public class Foo
{
private event EventHandler<Unit> _bang;
public IObservable<Unit> Bangs =>
Observable
.FromEventPattern<Unit>(h => _bang += h, h => _bang -= h)
.Select(x => x.EventArgs);
private Subject<Unit> _boom = new Subject<Unit>();
public IObservable<Unit> Booms =>
_boom.AsObservable();
public void OnExplode()
{
_bang?.Invoke(this, Unit.Default);
_boom.OnNext(Unit.Default);
}
}
现在我可以执行了:
var foo = new Foo();
foo.Bangs.Subscribe(_ => Console.WriteLine("Bang!"));
foo.Booms.Subscribe(_ => Console.WriteLine("Boom!"));
foo.OnExplode();
我在控制台上得到的结果是这样的:
Bang!
Boom!
代码很好地完成了工作。
现在,这两种方法的问题是 Foo
class 的恶意编码者。他们可以添加这个方法:
public void Nefarious()
{
_boom.OnCompleted();
_bang = null;
}
这有效地终止了代码。
现在我可以运行这个:
var foo = new Foo();
foo.Bangs.Subscribe(_ => Console.WriteLine("Bang!"));
foo.Booms.Subscribe(_ => Console.WriteLine("Boom!"));
foo.OnExplode();
foo.Nefarious();
foo.OnExplode();
我仍然只会看到一次输出。
两者都可以损坏。
但总的来说,我发现很少有人将他们的事件委托设置为 null
。
主题结束或出错的可能性要大得多,从而使代码表单停止工作。
我正在使用 System.Reactive
,但我不知道该选择哪一个:EventHandlers 或 Subjects。它们有什么区别?
var client = WebSocketClient.Create(uri);
// Subject
client.OnConnected
.Subscribe(_ => { Log.Information($"Socket {client.Id} connected"); })
.DisposeWith(disposable);
// EventHandler
Observable
.FromEventPattern(h => client.Connected += h, h => client.Connected -= h)
.Select(_ => Unit.Default)
.Subscribe(_ => { Log.Information($"Socket {client.Id} connected"); })
.DisposeWith(disposable);
public class WebSocketClient : IWebSocketClient
{
// Subject
private readonly ISubject<Unit> _connectedSubject = new Subject<Unit>();
public IObservable<Unit> OnConnected => _connectedSubject.AsObservable();
// EventHandler
private EventHandler? _connected;
public event EventHandler Connected
{
add => _connected += value;
remove => _connected -= value;
}
// Logic
async Task IWebSocketClient.ConnectAsync(CancellationToken cancellationToken)
{
...
await _webSocket.ConnectAsync(_uri, cancellationToken).ConfigureAwait(false);
_connected?.Invoke(this, EventArgs.Empty);
_connectedSubject.OnNext();
...
}
private void Dispose()
{
_connectedSubject.OnCompleted();
}
}
下面是一段代码示例,它更好地说明了使用主题和事件来创建可观察对象。
public class Foo
{
private event EventHandler<Unit> _bang;
public IObservable<Unit> Bangs =>
Observable
.FromEventPattern<Unit>(h => _bang += h, h => _bang -= h)
.Select(x => x.EventArgs);
private Subject<Unit> _boom = new Subject<Unit>();
public IObservable<Unit> Booms =>
_boom.AsObservable();
public void OnExplode()
{
_bang?.Invoke(this, Unit.Default);
_boom.OnNext(Unit.Default);
}
}
现在我可以执行了:
var foo = new Foo();
foo.Bangs.Subscribe(_ => Console.WriteLine("Bang!"));
foo.Booms.Subscribe(_ => Console.WriteLine("Boom!"));
foo.OnExplode();
我在控制台上得到的结果是这样的:
Bang!
Boom!
代码很好地完成了工作。
现在,这两种方法的问题是 Foo
class 的恶意编码者。他们可以添加这个方法:
public void Nefarious()
{
_boom.OnCompleted();
_bang = null;
}
这有效地终止了代码。
现在我可以运行这个:
var foo = new Foo();
foo.Bangs.Subscribe(_ => Console.WriteLine("Bang!"));
foo.Booms.Subscribe(_ => Console.WriteLine("Boom!"));
foo.OnExplode();
foo.Nefarious();
foo.OnExplode();
我仍然只会看到一次输出。
两者都可以损坏。
但总的来说,我发现很少有人将他们的事件委托设置为 null
。
主题结束或出错的可能性要大得多,从而使代码表单停止工作。