跳到主要内容

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