听起来您想要的是一种在您的方法中添加可空性前提条件的方法(即,如果我在字段 X、Y 或 Z 可能为空时调用此实例方法,请警告我)。语言在这一点上没有。欢迎您在https://github.com/dotnet/csharplang提出语言功能请求。
根据您的类型初始化的确切工作方式,将有不同的模式可以作为替代方案。听起来您有以下阶段:
- 使用一些参数调用构造函数,并将参数保存到字段中。
- 调用重载
Create()
并填充“后期初始化”字段。
Create()
调用Start()
,它几乎可以做其他所有事情。
在这种情况下,我会考虑将使用后期初始化字段的方法提取到另一种类型:
public class PdfCreator {
public void Create(FileInfo outputFile) {
var context = new PdfCreatorContext(new PdfWriter(outputFile));
context.Start();
}
public void Create(MemoryStream stream) {
var context = new PdfCreatorContext(new PdfWriter(stream));
context.Start();
}
private struct PdfCreatorContext
{
private PdfDoc doc;
internal PdfCreatorContext(PdfDoc doc)
{
this.doc = doc;
}
internal void Start() {
Method1();
// ...
MethodN();
}
internal void Method1() {
// Work with doc
doc.ToString();
}
// ...
internal void MethodN() {
// Work with doc
}
}
}
类的使用可能比这更复杂,或者异步和变异等问题使得使用struct
. 在这种情况下,您至少可以要求您的方法在编译器允许它们使用可空字段之前检查它们自己的先决条件:
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
public class PdfCreator {
PdfDoc? doc;
[Conditional("DEBUG"), MemberNotNull(nameof(doc))]
private void AssertInitialized()
{
Debug.Assert(doc != null);
// since the real thing has many nullable fields, we check them all
// in here, and reference them all in the MemberNotNull attribute.
}
private void Method1() {
AssertInitialized();
// Work with doc with the assumption it is not-null.
// In the case that any method is called with an unexpected
// null field in debug builds, we crash as early as possible.
doc.ToString();
}
private void Method2() {
// oops! we didn't AssertInitialized, so we get a warning.
doc.ToString();
}
}
请注意,[MemberNotNull]
目前仅在 .NET 5 预览版中可用。在 .NET Core 3 中,您可以编写一个 Debug.Assert 来检查调用站点所需的所有可为空的字段。
private void Method1() {
Debug.Assert(doc != null);
doc.ToString();
}