C# 反应式扩展延迟初始化
对于使用SimpleInjector解析的类型,在CTOR中进行工作是不好的做法,这一点已经相当确定。尽管这通常会导致此类类型的某些后期初始化,但一个特别有趣的情况是被动扩展订阅 例如,一个可观察的序列显示出C# 反应式扩展延迟初始化,c#,system.reactive,simple-injector,C#,System.reactive,Simple Injector,对于使用SimpleInjector解析的类型,在CTOR中进行工作是不好的做法,这一点已经相当确定。尽管这通常会导致此类类型的某些后期初始化,但一个特别有趣的情况是被动扩展订阅 例如,一个可观察的序列显示出Replay(1)语义(如果我们将StartWith考虑在内,实际上是BehaviorSubject),例如 private readonly IObservable\u myobbservable; 公共MyType(iSeries服务) { _myObservable=service.O
Replay(1)
语义(如果我们将StartWith
考虑在内,实际上是BehaviorSubject
),例如
private readonly IObservable\u myobbservable;
公共MyType(iSeries服务)
{
_myObservable=service.OtherObservable
.StartWith(service.Value)
.Select(x=>SomeTransform())
.重播(1)
.RefCount();
}
public IObservable myobbservable=>\u myobbservable;
现在假设SomeTransform
在计算上很昂贵。从SimpleInjector的角度来看,上述做法是不好的。好的,我们需要在SimpleInjector完成后调用某种Initialize()
方法。但是我们的重播语义和我们的StartWith()
呢?我们的消费者在订阅时希望得到一个值(现在假设这保证在初始化后发生)
我们如何在满足SimpleInjector的同时以一种好的方式绕过这些限制?以下是需求摘要:
SomeTransform
)\u myObservable
应为只读
MyObservable
应显示Replay(1)
语义StartWith
)订阅内部MyType
并缓存该值(我们喜欢不变性)
我尝试创建一个附加的可观察对象,它从
false
开始,然后在初始化时设置为true
,然后将其与\u myObservable
合并,但无法完全实现。此外,这似乎不是最好的解决方案。本质上,我所要做的就是延迟到Initialize()
完成。一定有什么方法可以做到这一点,但我看不到?想到的一个简单的解决方案是使用
这可能看起来像:
private readonly Lazy<IObservable<Value>> _lazyMyObservable;
public MyType(IService service)
{
_lazyMyObservable = new Lazy<IObservable<Value>>(() => this.InitObservable(service));
}
private IObservable<Value> InitObservable(IService service)
{
return service.OtherObservable
.StartWith(service.Value)
.Select(x => SomeTransform())
.Replay(1)
.RefCount();
}
public IObservable<Value> MyObservable => _lazyMyObservable.Value;
private readonly Lazy _lazyMyObservable;
公共MyType(iSeries服务)
{
_lazyMyObservable=newlazy(()=>this.InitObservable(服务));
}
私有IObservable InitObservable(iSeries服务)
{
返回服务。其他可观察的
.StartWith(service.Value)
.Select(x=>SomeTransform())
.重播(1)
.RefCount();
}
public IObservable MyObservable=>\u lazyMyObservable.Value;
这将初始化变量\u lazyMyObservable
,而不实际调用SomeTransform()
。当消费者要求MyType.MyObservable
时,InitObservable
代码将被调用一次且仅调用一次。这会将初始化延迟到实际使用代码的位置
这将使您的构造函数保持整洁,无需添加初始化逻辑
请注意,
Lazy
的ctor有几个重载,如果您可能有多线程问题,可以使用这些重载 想到的一个简单的解决方案是使用
这可能看起来像:
private readonly Lazy<IObservable<Value>> _lazyMyObservable;
public MyType(IService service)
{
_lazyMyObservable = new Lazy<IObservable<Value>>(() => this.InitObservable(service));
}
private IObservable<Value> InitObservable(IService service)
{
return service.OtherObservable
.StartWith(service.Value)
.Select(x => SomeTransform())
.Replay(1)
.RefCount();
}
public IObservable<Value> MyObservable => _lazyMyObservable.Value;
private readonly Lazy _lazyMyObservable;
公共MyType(iSeries服务)
{
_lazyMyObservable=newlazy(()=>this.InitObservable(服务));
}
私有IObservable InitObservable(iSeries服务)
{
返回服务。其他可观察的
.StartWith(service.Value)
.Select(x=>SomeTransform())
.重播(1)
.RefCount();
}
public IObservable MyObservable=>\u lazyMyObservable.Value;
这将初始化变量\u lazyMyObservable
,而不实际调用SomeTransform()
。当消费者要求MyType.MyObservable
时,InitObservable
代码将被调用一次且仅调用一次。这会将初始化延迟到实际使用代码的位置
这将使您的构造函数保持整洁,无需添加初始化逻辑
请注意,
Lazy
的ctor有几个重载,如果您可能有多线程问题,可以使用这些重载 注入构造函数应该是和。这意味着不赞成以下做法:
- 在构造函数内执行任何I/O操作。I/O操作可能会失败,并使对象图的构造不可靠
- 在构造函数中使用类的依赖项。被调用的依赖项不仅会导致其自身的I/O,有时注入的依赖项还没有(尚未)完全初始化,最终初始化会在稍后的时间点发生。也许在构建对象图之后
MyType
构造函数似乎没有执行任何I/O操作。在创建MyType
期间,不会调用其SomeTransform
方法。相反,可观察对象被配置为在推送对象时调用SomeTransform
。这意味着从DI的角度来看,您的注入仍然是“简单”和快速的。有时,您的类需要在存储传入依赖项的基础上进行一些初始化。例如,创建和存储一个惰性的
,就是一个很好的例子。它允许延迟执行一些I/O,同时仍然拥有比“接收依赖项”更多的代码
但您仍然在访问构造函数中的依赖项,如果该依赖项或其依赖项未完全初始化,则可能会导致问题。此外,使用反应式扩展,您可以从IService
返回到MyType
(您已经从