C# 如何处理类型化域模型中的可选数据?

C# 如何处理类型化域模型中的可选数据?,c#,typescript,null,api-design,defensive-programming,C#,Typescript,Null,Api Design,Defensive Programming,一般来说,在开发应用程序/领域模型时,我想知道处理不完整或可选数据的最佳方法是什么。 TypeScript和C等类型化语言为我们提供了输入模型的能力。有时这是很强大的,因为它对我们的模型施加了约束,可以强制它成为整数。 然而,通常情况下,现实生活中的数据是不完整的,这似乎大大降低了键入约束的优势 假设在一个名为Project的实体的示例应用程序(TypeScript前端和一些后端)中有一个简单的数据模型,该实体具有id、名称和可选描述。从后端检索数据 在前端使用以下接口定义数据模型: 导出接口i

一般来说,在开发应用程序/领域模型时,我想知道处理不完整或可选数据的最佳方法是什么。 TypeScript和C等类型化语言为我们提供了输入模型的能力。有时这是很强大的,因为它对我们的模型施加了约束,可以强制它成为整数。 然而,通常情况下,现实生活中的数据是不完整的,这似乎大大降低了键入约束的优势

假设在一个名为Project的实体的示例应用程序(TypeScript前端和一些后端)中有一个简单的数据模型,该实体具有id、名称和可选描述。从后端检索数据

在前端使用以下接口定义数据模型:

导出接口i项目{
id:编号;
名称:字符串;
描述:字符串;
}
从后端检索数据,如下所示:

导出类项目服务{
公共getProject(projectId:number):可观察{
常量url=http://server/api/project/“+projectd;
返回此.httpClient.get(url);
}
}
实际有描述的项目响应示例

{
    "id": 1
    "name": "My first project",
    "description": "My first project is just a demo"
}
alert(project.description.substr(0,10));
在前端应用程序中,我们显示检索到的数据。例如,让我们显示项目描述的前10个字符

{
    "id": 1
    "name": "My first project",
    "description": "My first project is just a demo"
}
alert(project.description.substr(0,10));
到目前为止,一切都很好

但是想象一下,我们的用户创建了“项目二”,而没有填写可选的描述。现在,后端可以响应:

{
    "id": 2
    "name": "Project two"
}

现在,我们在前端得到一个null引用异常:无法读取null的属性'substr'。 当然,可以添加一个if语句来检查描述是否为null:

if(project.description) {
  alert(project.description.substr(0,10));
}
这是可行的,但它意味着在整个应用程序中添加空检查。整个代码库将充满这些检查,以及掩盖错误而不是防止错误的危险。我觉得这不对

一种可能的解决方案是总是返回一个描述,因此如果没有填充,则返回一个空字符串

{
    "id": 2
    "name": "Project two",
    "description": ""
}
现在,前端不再需要对描述进行空检查,但不再能够区分显式填充的空描述和未填充的描述

{
    "id": 1
    "name": "My first project",
    "description": "My first project is just a demo"
}
alert(project.description.substr(0,10));
处理这类问题的最佳方法是什么?上面的例子只是为了说明,其范围相当一般。它似乎困扰着所有类型化语言,在这些语言中定义了一个不能始终实现的类型化的合同/接口。

在C#8中,您可以使用它来明确地将
描述
字段标记为可选1:

这里,我们将
Description
声明为
string?
——一个可能为空的字符串。然后,每当我们尝试在
Description
上调用一个首先检查
null
的方法时,编译器都会给我们一个警告

C#还提供了处理
null
的语法。例如,如果希望获取描述的前10个字符,但如果描述为
null
,则返回空字符串,则可以编写:

project.Description?.Substring(0, 10) ?? "";
(请注意,如果您的描述不为null,但长度小于10个字符,则会抛出此错误,因此在实践中需要一些额外的逻辑)

看起来Typescript有类似的概念


1此类将向您发出警告,因为
Name
不可为空,但也未初始化。有几种方法可以解决这个问题:给它一个默认值
;给
项目
一个设置
名称的构造函数
;在通过赋值
null对类型进行反序列化之前,强制中断不可为null的约定一段时间;其他一些。

在C#8中,您可以使用它显式地将
描述
字段标记为可选1:

这里,我们将
Description
声明为
string?
——一个可能为空的字符串。然后,每当我们尝试在
Description
上调用一个首先检查
null
的方法时,编译器都会给我们一个警告

C#还提供了处理
null
的语法。例如,如果希望获取描述的前10个字符,但如果描述为
null
,则返回空字符串,则可以编写:

project.Description?.Substring(0, 10) ?? "";
(请注意,如果您的描述不为null,但长度小于10个字符,则会抛出此错误,因此在实践中需要一些额外的逻辑)

看起来Typescript有类似的概念



1此类将向您发出警告,因为
Name
不可为空,但也未初始化。有几种方法可以解决这个问题:给它一个默认值
;给
项目
一个设置
名称的构造函数
;在通过赋值
null对类型进行反序列化之前,强制中断不可为null的约定一段时间;其他一些。

处理此类问题的最佳方法是什么?有什么问题吗。。。您正在展示javascript代码如何与C#连接?您可以发送相同的json和。。。在非类型化语言中,还必须检查是否给定了属性exists@Selvin“在非类型语言中,您还必须检查给定的属性是否存在”True,但在这些语言中没有契约;基本上,价值可以是任何东西;因此,检查属性是否存在是有意义的。在这种情况下,我使用类型化语言的问题是,当类型“Project”声称其合同中有描述属性时,需要检查属性是否存在。这个问题提出已经有一段时间了,但在许多语言中都有可选类型的概念。在TypeScript中,fp ts提供类型
选项
,该选项可以是
some(value)
none
。在Kotlin中,Arrow提供了同样的功能