在开发应用程序/领域模型时,我想知道处理不完整或可选数据的最佳方法是什么。TypeScript和C#等类型化语言为我们提供了键入模型的能力。有时这很强大,因为它对我们的模型施加了约束,并且可以强制它是整数。 但是,通常,现实生活中的数据是不完整的,这似乎大大降低了键入约束的优势。
想象一下,在一个名为Project的实体的示例应用程序(TypeScript前端和一些后端(中,一个简单的数据模型具有ID,名称和可选描述。数据是从后端检索的。
数据模型在前端使用接口定义:
export interface IProject {
id: number;
name: string;
description: string;
}
数据从后端检索,如下所示:
export class ProjectService {
public getProject(projectId: number): Observable<IProject> {
const url = 'http://server/api/project/' + projectId;
return this.httpClient.get<IProject>(url);
}
}
实际具有描述的项目的示例响应。
{
"id": 1
"name": "My first project",
"description": "My first project is just a demo"
}
在前端应用程序中,我们显示检索到的数据。例如,让我们显示项目描述的前 10 个字符。
alert(project.description.substr(0,10));
到目前为止,一切都很好。
但是想象一下,我们的用户在没有填写可选描述的情况下创建了"项目二"。现在,后端可以响应:
{
"id": 2
"name": "Project two"
}
或
{
"id": 2
"name": "Project two",
"description" : null
}
现在我们在前端得到一个空引用异常:无法读取 null 的属性 'substr'。 当然,可以添加一个 if 语句来检查描述是否为 null:
if(project.description) {
alert(project.description.substr(0,10));
}
这有效,但这意味着在整个应用程序中添加空检查。整个代码库将充满这些检查,以及掩盖错误而不是防止错误的危险。这对我来说是不对的。
一个可能的解决方案是始终返回描述,因此如果未填充任何描述,则返回空字符串。
{
"id": 2
"name": "Project two",
"description": ""
}
现在前端不再需要对描述进行空检查,但不再可能区分显式填充的空描述和尚未填写的描述。
处理这类问题的最佳方法是什么?上面的例子只是为了说明,范围非常通用。它似乎困扰着所有类型语言,其中定义了无法始终实现的类型化合约/接口。
在 C# 8 中,可以使用可为 null 的引用类型将description
字段显式标记为可选1:
class Project
{
public int Id { get; set; }
public string Name { get; set; }
public string? Description { get; set; }
}
在这里,我们将Description
声明为string?
- 一个可能为空的字符串。然后,每当我们尝试在Description
上调用一个方法时,首先检查它是否null
,编译器会给我们一个警告。
C# 还为您提供了使用null
的语法。例如,如果要获取描述的前 10 个字符,但如果描述null
则返回空字符串,则可以编写:
project.Description?.Substring(0, 10) ?? "";
(请注意,如果您的描述不为 null,但长度少于 10 个字符,则会抛出此值,因此在实践中您需要一些额外的逻辑(。
看起来打字稿也有类似的概念。
1此类会给你一个警告,因为Name
不可为空,但它也没有初始化。有几种方法可以解决这个问题:给它一个默认值""
;给Project
一个设置Name
的构造函数;在通过分配null!
对类型进行反序列化之前,在短时间内强制中断不可为空的协定;其他一些。