1

!在访问具有可选构造函数参数的字段时,我必须使用空检查覆盖。我想知道是否有更好的方法来做到这一点,我还没有意识到。

如果未提供参数,则此可选的、可为空的字段在构造函数主体中分配一个值。

class AuthService {
  http.Client? httpClient;
  
  AuthService({this.httpClient}) {
    httpClient ??= http.Client();
  }
}

稍后使用时httpClient我需要提供!,即使它永远不会为空......

http.Response response = await httpClient!.post(createUri,
          body: jsonEncode(requestBodyMap));

...否则飞镖分析仪告诉我: The method post cannot be unconditionally invoked because the receiver can be null

当我使用late关键字时,Lint 检查告诉我“assign-if-null”永远不会发生......

class AuthService {
  late http.Client httpClient;
  
  AuthService({this.httpClient}) {
    httpClient ??= http.Client(); // ← lint says this is useless & will never run
  }
}

...因为httpClient永远不能为空。

我应该忽略 Lint 吗?

我应该做一些完全不同的事情吗?

4

2 回答 2

0
class AuthService {
  late http.Client httpClient;
  
  AuthService({this.httpClient}) {
    httpClient ??= http.Client(); // ← lint says this is useless & will > never run
  }
}

那不应该是有效的。由于httpClient是不可为空的,AuthService({this.httpClient})意味着AuthService构造函数采用一个不能为空的可选命名参数,这是一个矛盾,因为可选参数必须是可空的。

无论如何,如果可能,您应该更喜欢在构造函数主体上使用初始化列表:

class AuthService {
  http.Client httpClient;
  
  AuthService({http.Client? httpClient})
    : httpClient = httpClient ?? http.Client();
}

这样做可以避免需要使用late(因为您可以更早地初始化成员)。

另请参阅https://stackoverflow.com/a/64548861/了解有关初始化列表与使用构造函数主体的更多详细信息。

于 2021-04-08T07:35:39.013 回答
0

多亏了 Google Dart 工程师和 jamesdlin 早先在这里得到答案的示例,这似乎有效

  • 使用永不为空的成员/字段实例变量:Client httpClient
  • 不要使用AuthService({this.httpClient})in constructor 的语法糖快捷方式
    • 在英语中这意味着:分配this.httpClient给我们的成员 varhttpClient或者null如果没有提供
  • 使用常规的、可能为空的构造函数 argAuthService({Client? httpClient})
  • 在初始化列表中,如果可能为空的 arg 不为空,则使用它,否则使用默认值:
    • : httpClient = httpClient ?? http.Client();

所以这:

不太好

class AuthService {
  http.Client? httpClient;
  
  AuthService({this.httpClient}) {
    httpClient ??= http.Client();
  }
}

...变成这样:

更好的

class AuthService {
  http.Client httpClient; // ← never null
  
  AuthService({http.Client? httpClient}) : httpClient = httpClient ?? http.Client(); 
  // ← no constructor body →
}

初始化列表

只是为了在初始化列表行上非常清楚:

// ↓↓ class field/member instance, never-null
: httpClient = httpClient ?? http.Client();
// constructor arg ↑↑ supplied, can be null

用英语:如果不为空,则将提供的客户端分配给我们的成员,否则实例化一个客户端并分配它

现在,该类不再需要对httpClient成员进行空检查或覆盖httpClient!.post().


我发现初始化列表很棘手,因为它们很难阅读,并且在执行时并不明显(在构造函数主体之前,将值分配给成员变量,this因为尚不存在实例而无法访问)。

此外,构造函数默认参数必须始终是常量。但是初始化列表赋值可以是非常量的计算值。

所以这是不允许的:

AuthService({this.httpClient = http.Client()})

...因为http.Client()不是一个常数。(如果它是一个常量构造函数,就可以了)。

但这很好:

AuthService({this.httpClient}) : httpClient = httpClient ?? http.Client();

在视觉上也很容易将成员实例与初始化列表中提供的构造函数参数混淆。没有语法高亮,很难阅读。 Android Studio 中的初始化列表语法高亮显示

↑ purple = member ↑ grey = constructor arg

查看jamesdlin 对内联、初始化器列表和构造器主体的细分,以便更好地理解所有这些内容。

于 2021-04-08T21:46:43.387 回答