-1

采用以下非空安全 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!";

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

不过,这似乎很棘手......所以在这些类型的场景中,将此代码转换为空安全的优雅且正确的方法是什么,如果有多种方法,那么优缺点是什么?

谢谢!

4

1 回答 1

2

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

1. 将变量初始化为一些非空标记值及assert以后:

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

如果您忽略AssertionErrorswitch.

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则添加一个 throws 允许编译器推断如果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.

(请注意,如果您在类型上使用语句,则不应default为此添加案例。对于s,编译器和分析器可以确定您的 s 是否详尽,如果您不小心省略了任何案例,则会生成分析警告。)switchenumenumcase


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

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

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-11-10T01:03:04.530 回答