需要RX的帮助。我想定义observable,它应该在创建第一个订阅时创建资源,为每个新订阅发布一次此资源实例,并且当完成所有订阅时,必须处理该资源实例。类似于Observable.Using,但具有Publish(value)和RefCount行为。我使用标准运算符表达它的所有尝试都失败了。这段代码做了我想要的,但我认为必须有标准的方法来做到这一点。我真的不想重新发明轮子。
using System;
using System.Reactive.Linq;
using System.Reactive.Disposables;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
// this part is what i can't express in standart RX operators..
Res res = null;
RefCountDisposable disp = null;
var @using = Observable.Create<Res>(obs =>
{
res = res ?? new Res();
disp = disp == null || disp.IsDisposed ? new RefCountDisposable(res) : disp;
obs.OnNext(res);
return new CompositeDisposable(disp.GetDisposable(), disp, Disposable.Create(() => res = null));
});
// end
var sub1 = @using.Subscribe(Print);
var sub2 = @using.Subscribe(Print);
sub1.Dispose();
sub2.Dispose();
sub1 = @using.Subscribe(Print);
sub2 = @using.Subscribe(Print);
sub1.Dispose();
sub2.Dispose();
Console.ReadKey();
}
static void Print(object o)
{
Console.WriteLine(o.GetHashCode());
}
}
class Res : IDisposable
{
public Res()
{
Console.WriteLine("CREATED");
}
public void Dispose()
{
Console.WriteLine("DISPOSED");
}
}
}
输出:
CREATED
1111
1111
DISPOSED
CREATED
2222
2222
DISPOSED
我对标准操作员的“最佳”尝试:
var @using = Observable.Using(() => new Res(), res => Observable.Never(res).StartWith(res))
.Replay(1)
.RefCount();
,输出为:
CREATED
1111
1111
DISPOSED
CREATED
1111 <-- this is "wrong" value
2222
2222
DISPOSED
谢谢!
PS。抱歉我的英语不好=(
答案 0 :(得分:2)
稍微头痛之后我终于意识到Using.Replay.RefCount
的问题是Replay
内部调用Multicast
单ReplaySubject
个实例,但在我的具体情况下我需要Replay
在每个新的首次订阅中重新创建主题。通过谷歌,我找到了RXX库,它的ReconnectableObservable
就是答案。它使用主题工厂而不是主题实例来重新创建每个Connect
调用中的主题(原始rxx代码,简单地没有合同):
internal sealed class ReconnectableObservable<TSource, TResult> : IConnectableObservable<TResult>
{
private ISubject<TSource, TResult> Subject
{
get { return _subject ?? (_subject = _factory()); }
}
private readonly object _gate = new object();
private readonly IObservable<TSource> _source;
private readonly Func<ISubject<TSource, TResult>> _factory;
private ISubject<TSource, TResult> _subject;
private IDisposable _subscription;
public ReconnectableObservable(IObservable<TSource> source, Func<ISubject<TSource, TResult>> factory)
{
_source = source;
_factory = factory;
}
public IDisposable Connect()
{
lock (_gate)
{
if (_subscription != null)
return _subscription;
_subscription = new CompositeDisposable(
_source.Subscribe(Subject),
Disposable.Create(() =>
{
lock (_gate)
{
_subscription = null;
_subject = null;
}
}));
return _subscription;
}
}
public IDisposable Subscribe(IObserver<TResult> observer)
{
lock (_gate)
{
return Subject.Subscribe(observer);
}
}
}
和一些扩展方法:
public static class Ext
{
public static IConnectableObservable<T> Multicast<T>(this IObservable<T> obs, Func<ISubject<T>> subjectFactory)
{
return new ReconnectableObservable<T, T>(obs, subjectFactory);
}
public static IConnectableObservable<T> ReplayReconnect<T>(this IObservable<T> obs, int replayCount)
{
return obs.Multicast(() => new ReplaySubject<T>(replayCount));
}
public static IConnectableObservable<T> PublishReconnect<T>(this IObservable<T> obs)
{
return obs.Multicast(() => new Subject<T>());
}
}
使用该代码,现在我可以这样做:
var @using = Observable
.Using(() => new Res(), _ => Observable.Never(_).StartWith(_))
.ReplayReconnect(1) // <-- that's it!
.RefCount();
雅虎!它按预期工作。
感谢所有回答的人!你把我推向了正确的方向。
答案 1 :(得分:0)
试试这个:
var @using = Observable.Using(
() => new Res(),
res => Observable.Return(res).Concat(Observable.Never<Res>()))
.Publish((Res)null)
.RefCount()
.SkipWhile(res => res == null);
当{oborable'产生唯一的值时,Concat
会阻止观察者自动取消订阅。
答案 2 :(得分:0)
如果有办法使用标准运算符,我看不到它。
问题是标准运营商没有“只有订户”的“缓存值 ”选项。
无论订阅者如何,重放运算符都将缓存最后一个值,并且是您看到的“错误”值的根本原因。
它强调了这样一个事实:使用+重放是一种危险的组合,因为它发出了一个处置值。
我怀疑如果某人使用标准运算符管理了一些魔法,它就不会像Observable.Create实现那样可读。
我已经多次使用Observable.Create创建代码,我确信它比使用标准运算符的等效构造更简洁,可读和可维护。
我的建议是使用Observable.Create绝对没有问题 - 用一个接受资源的漂亮的工厂方法包装你的代码,你很高兴。这是我尝试这样做的,它只是重新打包代码并增加了线程安全性:
public static IObservable<T> CreateObservableRefCountedResource<T>(Func<T> resourceFactory)
where T : class, IDisposable
{
T resource = null;
RefCountDisposable resourceDisposable = null;
var gate = new object();
return Observable.Create<T>(o =>
{
lock (gate)
{
resource = resource ?? resourceFactory();
var disposeAction = Disposable.Create(() =>
{
lock (gate)
{
resource.Dispose();
resource = null;
}
});
resourceDisposable = (resourceDisposable == null || resourceDisposable.IsDisposed)
? new RefCountDisposable(disposeAction)
: resourceDisposable;
o.OnNext(resource);
return new CompositeDisposable(
resourceDisposable,
resourceDisposable.GetDisposable());
}
});
}
已编辑 - 忘记调用resourceDisposable.GetDisposable()!