RX.Net中的主题是否总是有害的?

问题描述 投票:9回答:4

我正在和一位同事交谈,他向我指出了关于subjects being considered harmful的SO问题。但是,我有两种情况,我有一些非确定性代码,似乎没有任何其他方式合理。

非标准活动:

 event handler(class, result)
 {
   subject.OnNext(result);
 }

 public delegate void _handler
   ([MarshalAs(UnmanagedType.Interface), In] MyClass class, 
    [MarshalAs(UnmanagedType.Interface), In] ResultClass result)

并行任务(非确定性任务数量并行运行,从不同时间开始):

 Task.Start(()=> ...).ContinueWith(prevTask => subject.OnNext(prevTask.result))

只有通过观察才能暴露主体。有没有其他路线建议不是很多样板?

c# system.reactive reactive-programming
4个回答
6
投票

受试者并不总是有害的。即使在Rx本身内,它们也有许多合法用途。然而,很多时候一个人去使用主题,已经为该场景编写了一个强大的Rx方法(它可能会也可能不会在内部使用主题)。您的2个示例就是这种情况。查看Task.ToObservable和Observable.FromEventPattern。

另一个常见的案例主题是滥用,即开发人员将流分成两部分。他们确信他们需要订阅流,并且在回调中他们为新流生成数据。他们用主题做这件事。但通常他们应该使用Select代替。


1
投票

Observable.FromEvent

System.FromEvent不仅适用于内置事件类型:您只需要使用正确的重载。

class Program
{
    private static event Action<int> MyEvent;

    public static void Main(string[] args)
    {
        Observable.FromEvent<int>(
            (handler) => Program.MyEvent += handler,
            (handler) => Program.MyEvent -= handler
            )
            .Subscribe(Console.WriteLine);

        Program.MyEvent(5);

        Console.ReadLine();
    }
}

Task.ToObservable&Merge

如果您已经可以访问所有任务,则可以将它们转换为Observables,并将它们合并为单个observable。

class Program
{
    public static void Main(string[] args)
    {
        Observable.Merge(
                // Async / Await
                (
                    (Func<Task<string>>)
                    (async () => { await Task.Delay(250); return "async await"; })
                )().ToObservable(),
                // FromResult
                Task.FromResult("FromResult").ToObservable(),
                // Run
                Task.Run(() => "Run").ToObservable()
            )
            .Subscribe(Console.WriteLine);

        Console.ReadLine();
    }
}

合并Obsble

或者,如果您没有预先完成所有任务,您仍然可以使用Merge,但是您需要某种方式来传达未来的任务。在这种情况下,我使用了一个主题,但您应该使用最简单的Observable来表达这一点。如果这是一个主题,那么无论如何,使用一个主题。

class Program
{
    public static void Main(string[] args)
    {
        // We use a subject here since we don't have all of the tasks yet.
        var tasks = new Subject<Task<string>>();

        // Make up some tasks.
        var fromResult = Task.FromResult("FromResult");
        var run = Task.Run(() => "Run");
        Func<Task<string>> asyncAwait = async () => {
            await Task.Delay(250);
            return "async await";
        };

        // Merge any future Tasks into an observable, and subscribe.
        tasks.Merge().Subscribe(Console.WriteLine);

        // Send tasks.
        tasks.OnNext(fromResult);
        tasks.OnNext(run);
        tasks.OnNext(asyncAwait());

        Console.ReadLine();
    }
}

主题

为什么使用或不使用主题是一个我没有时间充分回答的问题。然而,通常来说,我发现当看起来运营商尚不存在时,使用主题往往是“简单的出路”。

如果你能以某种方式限制主体对应用程序其余部分的可见性,那么一定要使用主题并这样做。但是,如果您正在寻找消息总线功能,则应重新考虑应用程序的设计,因为消息总线是反模式的。


1
投票

受试者无害。这对我来说甚至可能有点过于教条(我首先要对主题的使用进行嘘声)。我会说受试者表示代码味道。如果没有它们,你可能会做得更好,但是如果你把它封装在你的课堂里,那么至少你要把气味保存在一个地方。

在这里,我想说,你已经在使用“非标准”事件模式,而且似乎你不想或不能改变它。在这种情况下,似乎主体作为桥梁的使用不会使它变得更糟。

如果你从头开始,那么我建议你深入思考你的设计,你可能会发现你不需要一个主题。

最后,我同意你应该使用FromEvent和ToTask的其他评论,但你建议这些不起作用。为什么?我不认为你提供足够的代码库来帮助解决这样的设计问题。例如你是如何创建非确定性任务的?什么?你想要解决的实际问题是什么?如果你能提供一个完整的例子,你可能会得到你正在寻找的关注量。


0
投票

以下是文档中关于它们为什么以及何时可能有害的说明:

http://www.introtorx.com/Content/v1.0.10621.0/18_UsageGuidelines.html

“避免使用主题类型.Rx实际上是一种函数式编程范例。使用主题意味着我们现在正在管理状态,这可能会发生变异。同时处理变异状态和异步编程很难做到。此外,许多运算符(扩展方法)都经过精心编写,以确保维护订阅和序列的正确和一致的生命周期;当您引入主题时,您可以打破这一点。如果您明确使用,未来版本也可能会出现严重的性能下降科目“。

© www.soinside.com 2019 - 2024. All rights reserved.