跳到主要內容

unsafe_variance

實驗性

不安全型別:在非協變位置包含型別變數。

詳細資訊

#

一個例項變數,如果其型別在非協變位置包含其所在類、混入或列舉的型別引數,則很可能由於型別檢查失敗而導致執行時錯誤。例如,在 class C<X> {...} 中,形式為 void Function(X) myVariable; 的例項變數可能會導致此類執行時錯誤。

對於返回型別在其所在宣告的型別引數中存在非協變位置的 getter 或方法,情況也是如此。

此 lint 規則會標記此類成員宣告。

不良示例

dart
class C<X> {
  final bool Function(X) fun; // LINT
  C(this.fun);
}

void main() {
  C<num> c = C<int>((i) => i.isEven);
  c.fun(10); // Throws.
}

問題在於 Xfun 的型別中作為引數型別出現。

減少執行時型別錯誤潛力的一種方法是確保非協變成員 fun this 上使用。我們無法嚴格強制執行此操作,但可以將其設為私有,並新增一個轉發方法 fun,以便可以在同一庫中本地檢查此約束是否得到滿足。

更好示例

dart
class C<X> {
  // ignore: unsafe_variance
  final bool Function(X) _fun;
  bool fun(X x) => _fun(x);
  C(this._fun);
}

void main() {
  C<num> c = C<int>((i) => i.isEven);
  c.fun(10); // Succeeds.
}

完全安全的方法需要 Dart 尚未具備的一個特性,即靜態檢查的方差 (variance)。有了它,我們可以指定型別引數 X 是不變的 (inout X)。

在沒有靜態檢查方差支援的情況下,也可以模擬不變性。這會對子型別的建立施加一些限制,但能忠實地提供 inout 所帶來的型別行為。

良好示例

dart
typedef Inv<X> = X Function(X);
typedef C<X> = _C<X, Inv<X>>;

class _C<X, Invariance extends Inv<X>> {
  // ignore: unsafe_variance
  final bool Function(X) fun; // Safe!
  _C(this.fun);
}

void main() {
  C<int> c = C<int>((i) => i.isEven);
  c.fun(10); // Succeeds.
}

使用這種方法,C<int> 不是 C<num> 的子型別,因此 c 必須具有不同的宣告型別。

另一種可能性是將變數宣告為一個安全但更通用的型別。這樣使用變數本身是安全的,但每次呼叫都必須在執行時進行檢查。

誠實示例

dart
class C<X> {
  final bool Function(Never) fun;
  C(this.fun);
}

void main() {
  C<num> c = C<int>((int i) => i.isEven);
  var cfun = c.fun; // Local variable, enables promotion.
  if (cfun is bool Function(int)) cfun(10); // Succeeds.
  if (cfun is bool Function(bool)) cfun(true); // Not called.
}

啟用

#

要啟用 unsafe_variance 規則,請在 analysis_options.yaml 檔案的 linter > rules 下新增 unsafe_variance

analysis_options.yaml
yaml
linter:
  rules:
    - unsafe_variance

如果您使用 YAML 對映語法配置 linter 規則,請在 linter > rules 下新增 unsafe_variance: true

analysis_options.yaml
yaml
linter:
  rules:
    unsafe_variance: true