Execute
部分中使用了可为 null 的属性,并且该属性已在
CanExecute
方法中针对 null 进行了检查,那么处理该属性的
CS8602: Possible dereference of null
警告的正确方法是什么?
[ObservableProperty, NotifyCanExecuteChangedFor(nameof(FooCommand)]
private FooType? _fooObject;
[RelayCommand(CanExecute = nameof(CanExecuteFooCommand)]
private void Foo()
{
SomeMethod(FooObject.ID); // gives CS8602: Possible dereference of null
}
private bool CanExecuteFooCommand()
{
return FooObject is not null;
}
private void SomeMethod(Guid? fooObject)
{
}
您认为哪种方式是正确的?对
FooObject
中的
Execute
进行另一个空检查?在
?
SomeMethod(FooObject?.ID)
在 !
SomeMethod(FooObject!.ID)
或者还有其他我不知道的方法吗?
FooObject
是否可以在
CanExecute
和
Execute
部分之间变为空?
CS8602只是来自静态分析器的警告。它不会从语义上分析您的代码。它不知道该变量永远不会为空 - 至少在代码或执行路径的这一点上。您将变量 FooObject
标记为可为空,因此分析器会显示警告“可能取消引用 null”。 您可以使用 null-forgiving 运算符
!
告诉分析器您已意识到风险,但要知道,虽然理论上是可能的,但此时该值永远不会是
null
。请注意,零容忍运算符只会使分析器静音。如果您重构代码,例如更改流程,现在允许变量为
null
,那么您的代码将会抛出异常。因此,在未来的代码更改后,始终存在变量永远不会
null
的保证变得无效的风险。我认为出于这个原因,应该谨慎或谨慎地使用它,因为分析器可空检查可能非常有帮助。我想大多数时候使用编译器检查来满足分析器的要求会增加必要的稳健性。例如,模式匹配就是一个很好的方法。有很多方法可以检查
null
。抛出
ArgumentNullException
异常也是一种增加鲁棒性的选项,因为这可以防止
null
参数值(并且也满足分析器的要求)。
“在 CanExecute 和 Execute 部分之间 FooObject 是否会变为 null?”如果您可以保证
ICommand.CanExecute
始终在
ICommand.Execute
之前直接调用(WPF
ICommandSource
实现就是这种情况,如
Button
和成员变量),那么您的
Execute
实现引用不会在之间共享线程,那么在这种情况下您可以安全地使用 null-forgiving 运算符,否则,我将显式执行编译器检查。
private FooType? _fooObject;
private void Foo()
{
// To add robustness, especially in anticipation of future changes,
// you probably want to let the compiler check the property
// e.g. by using pattern matching
if (_fooObject is not null)
{
SomeMethod(_fooObject.ID);
}
}
// Guid is a struct. As such, it can NEVER be NULL!
// The NULL value would be implicitly converted to default(Guid)
// However, when you make it nullable you must check the parameter to avoid bugs
private void SomeMethod(Guid? fooObject)
{
if (fooObject == Guid.Empty || fooObject == default)
{
return;
}
}