【发布时间】:2021-09-27 14:47:53
【问题描述】:
考虑一个有 2 个构造函数的类。假设一个构造函数接受一个依赖项,而另一个接受一个工厂以便稍后获得依赖项:
class ThereCanBeOnlyOneButAtLeastOne
{
readonly Thing? knownThing;
readonly ThingFactory? factory;
public ThereCanBeOnlyOneButAtLeastOne(ThingFactory factory) => this.factory = factory;
public ThereCanBeOnlyOneButAtLeastOne(Thing knownThing) => this.knownThing = knownThing;
public OtherThing CalculateSomething()
{
var theThing = knownThing??factory?.Get();
我希望静态分析能够确定此时 theThing 被保证为 NotNull,因为必须至少调用了一个构造函数。但事实并非如此。
可以吗?
【问题讨论】:
-
注意
theThing的类型依赖于工厂的Get方法的返回类型,请确保它返回一个不可为空的类型。 -
确实如此。否则我的问题会有缺陷:-)
-
不能指望静态分析深入这个兔子洞。这是一种对我们来说听起来“合理”但在实践中需要不平凡的规则的事情。我认为解决这个问题的一种简单且优雅的方法是始终只有
ThingFactory,并使用始终返回knownThing的SameThingFactory(或者如果整个类过于繁重,则将两者封装为委托)。当然还有其他统一逻辑的方法,至少需要一个 null 覆盖,但如果有必要,只需要一次。 -
请注意,仅使用
factory?.Get()就足以使静态分析认为您必须比它更了解可空性,因为它会假设结果可能是null,即使使用不可为空的factory甚至当knownThing根本不涉及。覆盖方法是knownThing ?? factory!.Get(),因为“我们”知道如果knownThing是null,则factory一定不是(这也是为什么在这里使用?.可能是不合适的)。但是请注意,这个假设很脆弱,如果我们添加另一个构造函数,很容易失败。 -
委托方法将是
readonly Func<Thing> getThing,分配为() => knownThing或factory.Get。然后knownThing和factory字段都成为可选的,删除它们将强制方法使用getThing(而不是在应该使用另一个时无意中使用一个)。当然,保留它们可能是有原因的(您需要将它们传递下去,或者对于单个对象而不是工厂确实有单独的逻辑)。