将 DTO 类 与 ORM 一起使用时 C#8 中的可空引用类型
Nullable reference type in C#8 when using DTO classes with an ORM
我在具有数据传输对象 (DTO) classes 的项目中激活了此功能,如下所示:
public class Connection
{
public string ServiceUrl { get; set; }
public string? UserName { get; set; }
public string? Password { get; set; }
//... others
}
但是我得到错误:
CS8618
: Non-nullable property 'ServiceUrl' is uninitialized. Consider declaring the property as nullable.
这是一个 DTO class,所以我没有初始化属性。这将是初始化 class 以确保属性非空的代码的责任。
例如,调用者可以这样做:
var connection = new Connection
{
ServiceUrl=some_value,
//...
}
我的问题:当启用 C#8 的可空性上下文时,如何处理 DTO classes 中的此类错误?
如果它是不可空的,那么编译器在初始化对象时可以做什么?
字符串的默认值为空,所以你会
要么需要在声明中指定一个字符串默认值
public string ServiceUrl { get; set; } = String.Empty;
或者在默认构造函数中初始化值这样就可以去掉警告
使用 !
运算符(您不能使用)
如 robbpriestley 所述使其可为空。
您可以执行以下任一操作:
EF Core 建议 initializing to null!
with null-forgiving operator
public string ServiceUrl { get; set; } = null! ;
//or
public string ServiceUrl { get; set; } = default! ;
使用支持字段:
private string _ServiceUrl;
public string ServiceUrl
{
set => _ServiceUrl = value;
get => _ServiceUrl
?? throw new InvalidOperationException("Uninitialized property: " + nameof(ServiceUrl));
}
在某些情况下可能会派上用场的另一件事:
[SuppressMessage("Compiler", "CS8618")]
可以在成员或整个类型之上使用。
还要考虑的另一件事是在文件顶部添加 #nullable disable
以禁用整个文件的可空引用。
Nullable 引用类型的好处是可以有效地管理空引用异常。所以最好使用指令#nullable enable。它帮助开发人员在 运行 时间避免空引用异常。
如何帮助避免空引用异常:
编译器将在静态流分析期间确保以下两件事。
- 该变量已明确分配给非空值。
- 变量或表达式之前已经过空值检查
取消引用它。
如果不满足以上条件,编译器会抛出警告。
注意:所有这些活动都是在编译期间发出的。
空引用类型有什么特别之处:
在 C# 8.0 之后,引用类型被认为是不可空的。这样,如果引用类型变量未正确处理空值,编译器会及时发出警告。
如果我们知道引用变量是 NULLABLE,我们可以做什么:
- 附加'?'在变量(示例)字符串之前?姓名
- 使用 "null forgiving operator"(示例)字符串名称 {get;set;} =
无效的!;或字符串名称 {get;set;} = default!;
使用"backing fields"
(示例):
私有字符串_name;
public 字符串名称 {get { return _name; } 设置 {_name = value;} }
如果我们知道引用变量是不可空的,我们可以做什么:
如果它不可为空,则使用构造函数初始化 属性。
不可空引用类型的好处:
- 更好地处理空引用异常
- 借助编译时警告,开发人员可以及时更正他们的代码。
- 开发人员在设计 class 时会将意图传达给编译器。
此后,编译器将通过代码执行意图。
通常 DTO 类 存储在单独的文件夹中,因此我只是根据路径模式在 .editorconfig 文件中禁用此诊断:
[{**/Responses/*.cs,**/Requests/*.cs}]
# CS8618: Non-nullable field is uninitialized. Consider declaring as nullable.
dotnet_diagnostic.CS8618.severity = none
我已经使用新的 Nullable Reference Types
(NRT) 功能一段时间了,我必须承认我最大的抱怨是编译器是在 classes 的声明中给你这些警告。
在我的工作中,我构建了一个 micro-service 试图解决所有这些导致相当复杂的代码的警告,尤其是在处理作为 .NET Core 消费者的 Nuget 包共享的 EF Core、AutoMapper 和 DTO 时.
这个非常简单的 micro-service 很快就变得一团糟,只是因为 NRT 功能导致我疯狂 non-popular 编码风格。
然后我发现了很棒的SmartAnalyzers.CSharpExtensions.Annotations Nuget package after reading Cezary Piątek's article Improving non-nullable reference types handling。
这个 Nuget 包正在将 non-nullbable 责任转移到实例化对象的调用者代码,而不是 class 声明代码。
在他的文章中,他说我们可以通过在您的 .cs
文件之一中写入以下行来在整个程序集中激活此功能
[assembly: InitRequiredForNotNull]
例如,您可以将它放在 Program.cs
文件中,但我个人更喜欢直接在我的 .csproj
中激活它
<ItemGroup>
<AssemblyAttribute Include="SmartAnalyzers.CSharpExtensions.Annotations.InitRequiredForNotNullAttribute" />
</ItemGroup>
我还通过在我的 .editorconfig
文件
中设置将默认的 CSE001 Missing initialization for properties
错误更改为警告
[*.cs]
dotnet_diagnostic.CSE001.severity = warning
您现在可以像往常一样使用 Connection
class 而不会出现任何错误
var connection = new Connection()
{
ServiceUrl = "ServiceUrl"
};
只有一件事需要注意。让我们考虑一下您的 class 这样的
public class Connection
{
public string ServiceUrl { get; }
public string? UserName { get; }
public string? Password { get; }
public Connection(string serviceUrl, string? userName = null, string? password = null)
{
if (string.IsNullOrEmpty(serviceUrl))
throw new ArgumentNullException(nameof(serviceUrl));
ServiceUrl = serviceUrl;
UserName = userName;
Password = password;
}
}
在那种情况下,当您实例化对象时
var connection = new Connection("serviceUrl");
SmartAnalyzers.CSharpExtensions.Annotations
Nuget 包不会分析您的构造函数来检查您是否真的在初始化所有 non-nullable 引用类型。它只是简单地信任它并相信你在构造函数中做的事情是正确的。因此,即使您忘记了像这样的 non-nullable 成员,它也不会引发任何错误
public Connection(string serviceUrl, string? userName = null, string? password = null)
{
if (string.IsNullOrEmpty(serviceUrl))
throw new ArgumentNullException(serviceUrl);
UserName = userName;
Password = password;
}
我希望你会喜欢这个 Nuget 包背后的想法,它已经成为我在所有新的 .NET Core 项目中安装的默认包。
要消除 DTO 上的警告,请在 DTO 的 cs 文件的开头指定:
#pragma 警告禁用 CS8618
最后:
#pragma 警告恢复 CS8618
我在具有数据传输对象 (DTO) classes 的项目中激活了此功能,如下所示:
public class Connection
{
public string ServiceUrl { get; set; }
public string? UserName { get; set; }
public string? Password { get; set; }
//... others
}
但是我得到错误:
CS8618
: Non-nullable property 'ServiceUrl' is uninitialized. Consider declaring the property as nullable.
这是一个 DTO class,所以我没有初始化属性。这将是初始化 class 以确保属性非空的代码的责任。
例如,调用者可以这样做:
var connection = new Connection
{
ServiceUrl=some_value,
//...
}
我的问题:当启用 C#8 的可空性上下文时,如何处理 DTO classes 中的此类错误?
如果它是不可空的,那么编译器在初始化对象时可以做什么?
字符串的默认值为空,所以你会
要么需要在声明中指定一个字符串默认值
public string ServiceUrl { get; set; } = String.Empty;
或者在默认构造函数中初始化值这样就可以去掉警告
使用
!
运算符(您不能使用)如 robbpriestley 所述使其可为空。
您可以执行以下任一操作:
EF Core 建议 initializing to
null!
with null-forgiving operatorpublic string ServiceUrl { get; set; } = null! ; //or public string ServiceUrl { get; set; } = default! ;
使用支持字段:
private string _ServiceUrl; public string ServiceUrl { set => _ServiceUrl = value; get => _ServiceUrl ?? throw new InvalidOperationException("Uninitialized property: " + nameof(ServiceUrl)); }
在某些情况下可能会派上用场的另一件事:
[SuppressMessage("Compiler", "CS8618")]
可以在成员或整个类型之上使用。
还要考虑的另一件事是在文件顶部添加 #nullable disable
以禁用整个文件的可空引用。
Nullable 引用类型的好处是可以有效地管理空引用异常。所以最好使用指令#nullable enable。它帮助开发人员在 运行 时间避免空引用异常。
如何帮助避免空引用异常: 编译器将在静态流分析期间确保以下两件事。
- 该变量已明确分配给非空值。
- 变量或表达式之前已经过空值检查 取消引用它。
如果不满足以上条件,编译器会抛出警告。 注意:所有这些活动都是在编译期间发出的。
空引用类型有什么特别之处:
在 C# 8.0 之后,引用类型被认为是不可空的。这样,如果引用类型变量未正确处理空值,编译器会及时发出警告。
如果我们知道引用变量是 NULLABLE,我们可以做什么:
- 附加'?'在变量(示例)字符串之前?姓名
- 使用 "null forgiving operator"(示例)字符串名称 {get;set;} = 无效的!;或字符串名称 {get;set;} = default!;
使用"backing fields" (示例):
私有字符串_name; public 字符串名称 {get { return _name; } 设置 {_name = value;} }
如果我们知道引用变量是不可空的,我们可以做什么:
如果它不可为空,则使用构造函数初始化 属性。
不可空引用类型的好处:
- 更好地处理空引用异常
- 借助编译时警告,开发人员可以及时更正他们的代码。
- 开发人员在设计 class 时会将意图传达给编译器。 此后,编译器将通过代码执行意图。
通常 DTO 类 存储在单独的文件夹中,因此我只是根据路径模式在 .editorconfig 文件中禁用此诊断:
[{**/Responses/*.cs,**/Requests/*.cs}]
# CS8618: Non-nullable field is uninitialized. Consider declaring as nullable.
dotnet_diagnostic.CS8618.severity = none
我已经使用新的 Nullable Reference Types
(NRT) 功能一段时间了,我必须承认我最大的抱怨是编译器是在 classes 的声明中给你这些警告。
在我的工作中,我构建了一个 micro-service 试图解决所有这些导致相当复杂的代码的警告,尤其是在处理作为 .NET Core 消费者的 Nuget 包共享的 EF Core、AutoMapper 和 DTO 时. 这个非常简单的 micro-service 很快就变得一团糟,只是因为 NRT 功能导致我疯狂 non-popular 编码风格。
然后我发现了很棒的SmartAnalyzers.CSharpExtensions.Annotations Nuget package after reading Cezary Piątek's article Improving non-nullable reference types handling。
这个 Nuget 包正在将 non-nullbable 责任转移到实例化对象的调用者代码,而不是 class 声明代码。
在他的文章中,他说我们可以通过在您的 .cs
文件之一中写入以下行来在整个程序集中激活此功能
[assembly: InitRequiredForNotNull]
例如,您可以将它放在 Program.cs
文件中,但我个人更喜欢直接在我的 .csproj
中激活它
<ItemGroup>
<AssemblyAttribute Include="SmartAnalyzers.CSharpExtensions.Annotations.InitRequiredForNotNullAttribute" />
</ItemGroup>
我还通过在我的 .editorconfig
文件
CSE001 Missing initialization for properties
错误更改为警告
[*.cs]
dotnet_diagnostic.CSE001.severity = warning
您现在可以像往常一样使用 Connection
class 而不会出现任何错误
var connection = new Connection()
{
ServiceUrl = "ServiceUrl"
};
只有一件事需要注意。让我们考虑一下您的 class 这样的
public class Connection
{
public string ServiceUrl { get; }
public string? UserName { get; }
public string? Password { get; }
public Connection(string serviceUrl, string? userName = null, string? password = null)
{
if (string.IsNullOrEmpty(serviceUrl))
throw new ArgumentNullException(nameof(serviceUrl));
ServiceUrl = serviceUrl;
UserName = userName;
Password = password;
}
}
在那种情况下,当您实例化对象时
var connection = new Connection("serviceUrl");
SmartAnalyzers.CSharpExtensions.Annotations
Nuget 包不会分析您的构造函数来检查您是否真的在初始化所有 non-nullable 引用类型。它只是简单地信任它并相信你在构造函数中做的事情是正确的。因此,即使您忘记了像这样的 non-nullable 成员,它也不会引发任何错误
public Connection(string serviceUrl, string? userName = null, string? password = null)
{
if (string.IsNullOrEmpty(serviceUrl))
throw new ArgumentNullException(serviceUrl);
UserName = userName;
Password = password;
}
我希望你会喜欢这个 Nuget 包背后的想法,它已经成为我在所有新的 .NET Core 项目中安装的默认包。
要消除 DTO 上的警告,请在 DTO 的 cs 文件的开头指定: #pragma 警告禁用 CS8618
最后: #pragma 警告恢复 CS8618