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的范围是一种类型.
 - 您不需要定义类似的事件,也不存在类似的事件.
 在这种情况下,我为什么要使用主题?
因为你别无选择!
因此,回答"为什么使用这样的主题的细节是一个坏主意"的内在问题 - 这不是一个坏主意,这是使用主题的少数几个地方之一是正确的做事方式.