Wil*_*lka 26 c# system.reactive
在这个关于关于Subject<T>Enigmativity 的问题的答案中提到:
另外,你应该尽量避免使用主题.一般规则是,如果你正在使用一个主题,那么你做错了什么.
我经常使用主题作为IObservable属性的支持字段,这可能是在Rx之前的几天中的.NET事件.例如,而不是像
public class Thing
{
public event EventHandler SomethingHappened;
private void DoSomething()
{
Blah();
SomethingHappened(this, EventArgs.Empty);
}
}
Run Code Online (Sandbox Code Playgroud)
我可能会这样做
public class Thing
{
private readonly Subject<Unit> somethingHappened = new Subject<Unit>();
public IObservable<Unit> SomethingHappened
{
get { return somethingHappened; }
}
private void DoSomething()
{
Blah();
somethingHappened.OnNext(Unit.Default);
}
}
Run Code Online (Sandbox Code Playgroud)
那么,如果我想避免使用Subject什么是正确的方式来做这种事情?或者我应该坚持在我的界面中使用.NET事件,即使它们被Rx代码使用(很可能FromEventPattern)?
此外,更多详细信息,为什么使用Subject这样一个坏主意将是有帮助的.
更新:为了使这个问题更加具体,我正在讨论使用Subject<T>从非Rx代码(可能是你正在使用其他一些遗留代码)到Rx世界的方法.所以,像:
class MyVolumeCallback : LegacyApiForSomeHardware
{
private readonly Subject<int> volumeChanged = new Subject<int>();
public IObservable<int> VolumeChanged
{
get
{
return volumeChanged.AsObservable();
}
}
protected override void UserChangedVolume(int newVolume)
{
volumeChanged.OnNext(newVolume);
}
}
Run Code Online (Sandbox Code Playgroud)
LegacyApiForSomeHardware类型不是使用事件,而是使用覆盖虚拟方法作为获取"刚刚发生"通知的方式.
小智 17
首先,有人可以将SomethingHappened转回ISubject并从外部向其中提供东西.至少应用AsObservable它来隐藏底层对象的主体.
此外,回调的主题广播并不比.NET事件更严格.例如,如果一个观察者抛出,则不会调用链中下一个观察者.
static void D()
{
Action<int> a = null;
a += x =>
{
Console.WriteLine("1> " + x);
};
a += x =>
{
Console.WriteLine("2> " + x);
if (x == 42)
throw new Exception();
};
a += x =>
{
Console.WriteLine("3> " + x);
};
a(41);
try
{
a(42); // 2> throwing will prevent 3> from observing 42
}
catch { }
a(43);
}
static void S()
{
Subject<int> s = new Subject<int>();
s.Subscribe(x =>
{
Console.WriteLine("1> " + x);
});
s.Subscribe(x =>
{
Console.WriteLine("2> " + x);
if (x == 42)
throw new Exception();
});
s.Subscribe(x =>
{
Console.WriteLine("3> " + x);
});
s.OnNext(41);
try
{
s.OnNext(42); // 2> throwing will prevent 3> from observing 42
}
catch { }
s.OnNext(43);
}
Run Code Online (Sandbox Code Playgroud)
通常,一旦观察者抛出,调用者就会死亡,除非你保护每个On*调用(但不要随意吞下异常,如上所示).多播代理也是如此; 例外情况会向你回转.
大多数情况下,您可以在没有主题的情况下实现您想要做的事情,例如使用Observable.Create来构建新序列.这样的序列没有由多个订阅产生的"观察者列表"; 每个观察者都有自己的"会话"(寒冷的可观察模型),因此观察者的例外仅仅是在一个狭窄的区域内的自杀命令,而不是在一个广场中间吹嘘自己.
本质上,主题最好用在反应式查询图的边缘(对于需要由数据中的另一方提供的入口流,尽管您可以使用常规.NET事件并使用FromEvent将它们连接到Rx*方法)和用于在被动查询图中共享订阅(使用发布,重放等,这是伪装的多播调用,使用主题).使用主题的危险之一 - 由于其观察者列表和潜在的消息记录而非常有状态 - 是在尝试使用主题编写查询运算符时使用它们.99.999%的时间,这样的故事有一个悲伤的结局.
Wil*_*lka 11
在Rx论坛上的回答中,Dave Sexton(Rxx)说,作为答案的一部分:
主题是Rx的有状态组件.当您需要创建类似事件的observable作为字段或局部变量时,它们非常有用.
这正是这个问题正在发生的事情,他还撰写了一篇关于使用主题或不使用主题的深入跟进博客文章?最后得出:
我什么时候应该使用一个主题?
如果满足以下所有条件:
- 你没有可观察的东西或任何可以转换成一个的东西.
- 你需要一个热的观察.
- 您的observable的范围是一种类型.
- 您不需要定义类似的事件,也不存在类似的事件.
在这种情况下,我为什么要使用主题?
因为你别无选择!
因此,回答"为什么使用这样的主题的细节是一个坏主意"的内在问题 - 这不是一个坏主意,这是使用主题的少数几个地方之一是正确的做事方式.