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