【问题标题】:How best to convert old Dart code that triggers "The non-nullable variable must be assigned" error?如何最好地转换触发“必须分配不可为空的变量”错误的旧 Dart 代码?
【发布时间】:2021-12-22 14:25:41
【问题描述】:

采用以下非空安全 Dart 代码:

static String appBarShiftTitleString(int fromEpochSeconds) {
    String monthWord;  
    String dayWord;

    DateTime dt = DateTime.fromMillisecondsSinceEpoch(fromEpochSeconds * 1000);

    switch (dt.month) {
      case 1:
        monthWord = "Jan";
        break;
      case 2:
        monthWord = "Feb";
        break;
      case 3:
        monthWord = "Mar";
        break;
      case 4:
        monthWord = "Apr";
        break;
      case 5:
        monthWord = "May";
        break;
      case 6:
        monthWord = "Jun";
        break;
      case 7:
        monthWord = "Jul";
        break;
      case 8:
        monthWord = "Aug";
        break;
      case 9:
        monthWord = "Sep";
        break;
      case 10:
        monthWord = "Oct";
        break;
      case 11:
        monthWord = "Nov";
        break;
      case 12:
        monthWord = "Dec";
        break;
    }

    switch (dt.weekday) {
      case 1:
        dayWord = "Mon";
        break;
      case 2:
        dayWord = "Tue";
        break;
      case 3:
        dayWord = "Wed";
        break;
      case 4:
        dayWord = "Thu";
        break;
      case 5:
        dayWord = "Fri";
        break;
      case 6:
        dayWord = "Sat";
        break;
      case 7:
        dayWord = "Sun";
        break;
    }

    return dayWord + ' ' + monthWord + ' ' + dt.day.toString();
}

Android Studio 说,“必须先分配不可为空的局部变量 'dayWord',然后才能使用它。”

我理解错误并发现我可以像这样简单地修改方法的前两行:

String monthWord = "error!";
String dayWord = "error!";

这样,我满足了语言规则,如果我们达到了变量未被分配的不可能的情况,那将是显而易见的。

虽然这看起来很棘手......所以在这些类型的场景中,将这段代码转换为空安全的优雅和正确的方法是什么,如果有多种方法,那么优缺点是什么?

谢谢!

【问题讨论】:

  • 您是如何获得该代码的?你是在一个工作项目中使用 Dart 迁移,还是这是手工制作的?因为如果那是遗留代码,它应该可以正常运行,除非您手动将 pubspec.yaml minsdk 更改为 2.12。
  • 这是我自己的旧项目代码。是的,我通过使用最新的 Android Studio+Flutter+Dart 版本创建一个新的样板 Flutter 项目,有效地将 min 更改为 2.12。然后我开始从旧项目中剪切和粘贴。这是故意的——我宁愿遇到错误并修复它们并在此过程中学习,而不是使用迁移工具。

标签: dart dart-null-safety null-safety


【解决方案1】:

一般来说,您有几个选择:

1。将变量初始化为一些非空标记值和assert 稍后:

String monthWord = '';
// ...
switch (dt.month) {
  // ...
}
assert(monthWord.isNotEmpty);

如果您忽略在switch 中处理它的案例,这将导致调试版本在运行时抛出AssertionError

2。使变量可为空并使用空断言运算符:

String? monthWord;
// ...
switch (dt.month) {
  // ...
}
monthWord!;

// Since `monthWord` is a local variable, it will now be promoted to a
// non-nullable `String` type.

如果您忽略将变量设置为非空值,这将在所有构建类型中抛出 TypeError

3。使变量late

将变量声明为late 表示您承诺在读取变量之前将对其进行初始化。当您尝试访问该变量时,编译器将生成运行时检查以验证该变量是否已初始化。如果您忽略设置变量,这将在所有构建类型中抛出 LateInitializationError

4。添加一个 default 抛出的案例

如果您的所有cases 都设置了一个局部变量,那么添加一个抛出的default 案例允许编译器推断如果到达switch 语句之后的代码必须始终设置该变量:

String monthWord; // No explicit initialization required!
// ...
switch (dt.month) {
  case 1:
    monthWord = "Jan";
    break;

  // ... etc. ...

  default:
    throw AssertionError('Unhandled case: ${dt.month}');  
}

// The compiler now can deduce that `monthWord` is guaranteed to be
// initialized.

(请注意,如果您在enum 类型上使用switch 语句,则应为此添加default 大小写。对于enums,编译器分析器可以确定您的cases 是否详尽,如果您不小心遗漏了任何情况,则会生成分析警告。)


至于使用哪种方法,这主要是一个偏好问题。它们大部分都是等效的,因为它们会导致运行时错误。我个人会选择 #1 (assert) 或 #4 (default case) 以避免在发布版本中进行不必要的检查。

在您的特定示例中,我也将分别使用 DateTime.monthDateTime.day 作为月份和日期名称的 Lists 的索引:

const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

assert(months.length == 12);
assert(days.length == 7);

var monthWord = months[dt.month - 1];
var dayWord = days [dt.day - 1];

【讨论】:

    猜你喜欢
    • 2021-12-22
    • 1970-01-01
    • 1970-01-01
    • 2021-07-06
    • 1970-01-01
    • 2022-07-19
    • 2023-03-31
    • 1970-01-01
    • 2021-10-23
    相关资源
    最近更新 更多