【发布时间】:2020-11-23 02:32:12
【问题描述】:
我的类有需要初始化的属性。由于我不想传递部分构造的对象,并且构造函数不能/不应该异步,我一直在使用 here 描述的工厂模式,所以我的类看起来像这样:
public class BaseClass
{
public PropType AsyncProp1 { get; set; }
public static async Task<BaseClass> CreateObject()
{
var baseClass = new BaseClass()
{
AsyncProp1 = await GetProp1Async()
};
return baseClass;
}
}
到目前为止,该模式对我很有帮助,但现在我需要实现其他类,这些类将扩展 BaseClass 并具有其他需要初始化的异步属性,然后才能使用它们的对象。
我最初的计划是简单地覆盖 CreateObject() 方法,但不能将静态方法设为虚拟/覆盖。然后我决定在基类中隐藏 CreateObject() 方法,并在派生类中添加一个新的 CreateObject() 方法,如下所示:
public class DerivedClass : BaseClass
{
public PropType AsyncProp2 { get; set; }
public static new async Task<DerivedClass> CreateObject()
{
var derivedClass = new DerivedClass()
{
AsyncProp1 = await GetProp1Async(),
AsyncProp2 = await GetProp2Async(AsyncProp1) // can't do that - AsyncProp1 is not static
};
return derivedClass;
}
}
不用说,对于每个派生类,我都需要重写继承属性的初始化。仅此一项就违背了整个继承概念,但最糟糕的是派生类中的一些新异步属性依赖于基类中的旧异步属性,但我不能简单地在 CreateObject() 中调用它们方法,因为它们不是静态属性。
我有什么办法可以改进我目前所拥有的,解决这个 Catch22 问题,并实现上述目标?
[编辑]
只是为了提供一点上下文,我不想依赖在我的类之外调用的初始化方法的原因是我可能无法控制创建对象和调用此类初始化方法的代码。这就是为什么我宁愿将一个“准备使用”的对象返回给调用者。
【问题讨论】:
-
在创建对象后调用一个虚拟异步 Init 函数怎么样? (即不需要创建对象函数,只需要调用者调用异步初始化)
-
@Wyck 我相信你所提议的有点像here 中描述的“异步初始化模式”。即使没有 Task
,我也可以做到这一点,只需在我的调用者中使用 var newObject = new BaseClass(); 后跟 await newObject.InitAsync(); 。但正如我之前解释的那样,我宁愿避免这种情况。 -
我认为你应该阅读 Stephen Cleary 的 very next post 声明,让你的异步初始化对象有一个公开非异步属性 (
AsyncProp1) 的 api 是矛盾的。这相当于想要一个异步属性。您可以重新设计此对象的接口,使其任何方法也是异步的,并让这些方法在内部确保它已初始化(如有必要,等待 InitAsync)。 ...如果防止滥用是您的主要目标。 -
附注:我更喜欢单独的工厂类型(而不是静态的
CreateAsync)。但每种情况都不同;有时AsyncLazy<T>正是您想要的(缺点是读取的每个属性都需要await)。当我写这些博客文章时,我希望 DI 容器能够采用某种异步创建模式。然而,这似乎永远不会发生。 -
@StephenCleary 首先,感谢这些文章。我是异步编程的新手,他们帮了我很多。读完你的评论后,我回去查看它们,才意识到你是 8 年前写的!令人印象深刻的是,在这么长时间之后,它们如何保持有用和最新,尤其是在像我们这样的领域。再次感谢!
标签: c# inheritance constructor async-await static-methods