为了清楚起见,我想在 Dart 中使用命名参数。我该如何处理它们?

I want to use named parameters in Dart for clarity. How should I handle them?

TL;DR:由于 a conscious design choice,命名参数是可选的。在没有官方语言支持的情况下,有什么方法可以强制(和通知)所需的命名参数吗?


我发现在定义 class 时使用命名参数非常有用。以 MMORPG 中的 Ability 为例:

class Ability {

  final name;
  final effectDuration;
  final recast;            // wait time until next use
  // ...
}

effectDurationrecast 都携带相同类型的信息(即持续时间)并且可能由相同的数据类型表示。很容易搞混哪个号码去哪里。但是,它们都是对对象的正确性至关重要的信息,因此在实例化过程中不能丢失它们。

我可以通过 try-catch 来中断程序以强制执行这些参数的要求,但是对于使用 class 并且不知道(阅读不足)的人来说,这听起来并不有趣文档并直观地理解 class 的作用)它们是必需的。

有没有什么方法可以强制执行某些命名参数的要求,同时设法通知调用者所述要求and/or帮助他们正确使用它?

meta 包提供了 DartAnalyzer 支持的 @required 注解。

Flutter 经常使用它并直接从 import 'package:flutter/foundation.dart'

提供 @required
foo({@required String name}) {...}

foo(); // results in static warning

@required 不检查传递的值是否为 null,只检查调用站点上是否实际传递了一个值。 要检查 null,您还可以使用 assert() 检查传递的值

class Ability {
  Ability(this.name, this.effectDuration, this.recast) : assert(name != null), assert(effectDuration != null), assert(recast != null);
  final name;
  final effectDuration;
  final recast;            // wait time until next use
  // ...
}    

[更新] 新 as-of Dart 2.0

在 dart 2.0 中,required 关键字已作为 null-safety 更新的一部分添加到语言中。这意味着您得到的是 compiler-enforced non-null 值,而不是分析器检查的值;这使得空检查完全多余。

这意味着此代码实际上与下面的旧代码相同,除了您永远不必担心断言作为 nameeffectDuration 和 [= 的值抛出15=] 不能为空。

class Ability {
  final String name;
  final Duration effectDuration;
  final bool recast;
  final String? description;

  Ability({
    required this.name,
    this.effectDuration = Duration(seconds: 1),
    this.recast = false,
    this.description,
  });
}

Dart 2.0 之前

是的,有!

这是一个例子:

class Ability {
  final String name;
  final Duration effectDuration;
  final bool recast;
  final String description;

  Ability({
    @required this.name,
    this.effectDuration = new Duration(seconds: 1),
    this.recast = false,
    this.description,
  }): 
    assert(name != null),
    assert(effectDuration != null);
}

您不必断言名称不等于 null,但它可能对您有用。

虽然您可以按照接受的答案中的描述使用 flutter foundation 包,但当我使用不需要了解 Flutter 的模型 classes 时,我更喜欢使用meta 包直接。这样它就不会对框架产生不必要的依赖。这允许您甚至在 Flutter 之外共享 Dart 代码。

meta添加到pubspec.yaml:

dependencies:
  meta: ^1.1.7

将其导入您的 class 文件:

import 'package:meta/meta.dart';

在您的代码中使用 @required 注释:

class Person {
  String name;
  int age;

  Person({@required this.name, this.age,});
}

所以 name 是必需的参数,但 age 不是。

final person = Person(name: 'Bob');

更新:

在即将推出的 Dart 版本中,默认情况下应添加 required 关键字,因此根本不需要导入。

从 2.12 开始,您可以使用 required 关键字(而不是 @required)。也不需要导入任何额外的包。

在此示例中,命名参数 name 是可选的,而 effectDurationrecast 是必需的。

class Ability {
  final name;
  final effectDuration;
  final recast;

  Ability({this.name, required this.effectDuration, required this.recast});
}

更新pubspec.yaml,例如:

environment:
  sdk: ">=2.12.0-0  <3.0.0"

参考文献:

  1. Sound null safety
  2. How does @required compare to the new required keyword?

空安全:

  • 不可为 null 的命名参数:

    您需要标记命名参数 required 或提供默认值,甚至标记它 late。例如:

    class Foo {
      final int a;
      final int b;
      late final int c; // Mark late and provide value later.
    
      Foo({
        required this.a, // Mark required.
        this.b = 0, // Provided a default value.
      });
    }
    
  • 可为空的命名参数:

    你不需要任何特殊的东西来处理它们。

    class Foo {
      final int? z;
    
      Foo({
        this.z,
      });
    }
    

如果你想声明一个空变量但里面有方法,你可以:

1)使用late关键字

2) 尽可能声明类型 null 返回示例:int? number;

3)初始化变量为空,例如:

  List listOfNumbers = [];
  Map mapOfPerson1 = {};

所以你可以使用变量的方法来添加它们的值