我正在和一位同事交谈,他向我提出了关于subjects being considered harmful的问题。但是,我有两种情况,我有一些非确定性的代码,似乎没有任何其他方式合理。
非标准事件:
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))
只有通过观察才能暴露受试者。还有另一条路线建议不是很多样板吗?
答案 0 :(得分:6)
受试者并非总是有害的。即使在Rx本身内,它们也有许多合法用途。然而,很多时候一个人去使用主题,已经为该场景编写了一个强大的Rx方法(它可能会也可能不会在内部使用主题)。您的2个示例就是这种情况。查看Task.ToObservable和Observable.FromEventPattern。
另一个常见的案例主题被滥用是指开发人员将流分成两部分。他们确信他们需要订阅流,并且在回调中他们为新流生成数据。他们用主题做这件事。但通常他们应该使用Select代替。
答案 1 :(得分:1)
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();
}
}
如果您已经可以访问所有任务,则可以将它们转换为Observables,并将它们合并为一个可观察对象。
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();
}
}
或者,如果您没有预先完成所有任务,您仍然可以使用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();
}
}
为什么使用或不使用主题是一个我没有时间充分回答的问题。然而,通常来说,我发现当看起来操作员不存在时,使用主题往往是“简单的出路”。
如果您可以某种方式限制某个主体对其他应用程序的可见性,那么无论如何都要使用主题并且这样做。但是,如果您正在寻找消息总线功能,则应重新考虑应用程序的设计,因为消息总线是反模式的。
答案 2 :(得分:1)
受试者没有害处。这对我来说甚至可能有点过于教条(我首先要对主题的使用进行嘘声)。我会说受试者表示代码味道。如果没有它们,你可能会做得更好,但是如果你把它封装在你的班级中,那么至少你要把气味保存在一个地方。
我会说,你已经在使用&#34;非标准&#34;事件模式,似乎你不想或不能改变它。在这种情况下,似乎使用主体作为桥梁并不会使它变得更糟。
如果你是从头开始,那么我建议你深入思考你的设计,你可能会发现你不需要一个主题。
最后,我同意其他评论,你应该使用FromEvent和ToTask,但你建议这些不起作用。为什么?我不认为你提供足够的代码库来帮助解决这样的设计问题。例如你是如何创建非确定性任务的?什么?你想要解决的实际问题是什么?如果你能提供一个完整的例子,你可能会得到你正在寻找的关注量。
答案 3 :(得分:0)
以下是文档中说明其为何有害的原因:
http://www.introtorx.com/Content/v1.0.10621.0/18_UsageGuidelines.html
“避免使用主题类型。Rx实际上是一种功能 编程范例。使用主体意味着我们现在正在管理状态, 这可能会变异。处理变异状态和 同时很难进行异步编程。 此外,许多运算符(扩展方法)已经 仔细撰写以确保正确和一致的使用寿命 订阅和顺序得以维护;当你介绍 科目,你可以打破这一点。将来的发行版也可能会很重要 如果您明确使用主题,则会降低性能。”