Dart null/false/empty checking:如何写这个更短?

Bla*_*bam 15 conditional if-statement dart

这是我的代码,对于除了空字符串,null和false之外的所有内容都是true:

if (routeinfo["no_route"] == "" || routeinfo["no_route"] == null || routeinfo["no_route"] == false) {
    // do sth ...
}
Run Code Online (Sandbox Code Playgroud)

这是我的代码,对于除了空字符串,null,false或零之外的所有内容都是true:

if (routeinfo["no_route"] == "" || routeinfo["no_route"] == null || routeinfo["no_route"] == false || routeinfo["no_route"] == 0) {
    // do sth...
}
Run Code Online (Sandbox Code Playgroud)

我怎么能在Dart写这个更短的?还是不可能?

Jan*_*sen 37

如果您的要求只是空或为空(例如我在搜索结果中看到此标题时的要求),则可以使用Dart的安全导航操作符使其更为简洁:

if (routeinfo["no_route"]?.isEmpty ?? true) {
  // 
}
Run Code Online (Sandbox Code Playgroud)

哪里

  • isEmpty检查一个空的字符串,但是如果routeinfo是null,则不能在null上调用isEmpty,因此我们使用以下命令检查null
  • ?. 安全导航运算符,该运算符将仅在对象不为null时调用isEmpty,否则生成null。所以我们只需要检查null
  • ?? 空合并运算符

  • 另一种写法:`if((value ??'')==''){...}` (3认同)

om-*_*-ha 37

2020 年末更新

概括

  • 这个答案是正确的,除了isNullisNotNullNull Safety将来在 dart/flutter 中引入时,它们不再提供类型提升。
  • 其他像isNullOrEmpty这样的助手不提供类型提升,因为与 callsite 相比,它们处于不同的(子)范围内。
  • 我个人的意见是,您可以放弃isNullisNotNull保留其他助手,因为您不应该期望他们为您进行类型提升。

解释

示范

这里演示了为什么isNull( == null) 和isNotNull( != null) 的封装/helper-getter是一个非常大的问题:

// Promotion works
int definitelyInt(int? aNullableInt) {
  if (aNullableInt == null) { // Promote variable `aNullableInt` of Nullable type `int?` to Non-Nullable type `int`
    return 0;
  }
  return aNullableInt; // Can't be null! This variable is promoted to non-nullable type `int`
}
Run Code Online (Sandbox Code Playgroud)

当您使用的 dart 版本中包含“Null Safety”时,上述代码中的类型提升有效!然而:

// Promotion does NOT work!!!
int definitelyInt(int? aNullableInt) {
  if (aNullableInt.isNull) { // does NOT promote variable `aNullableInt` of Nullable type `int?`
    return 0;
  }
  return aNullableInt; // This variable is still of type `int?`!!!
}
Run Code Online (Sandbox Code Playgroud)

以上不起作用,因为空检查== null!= null被封装在一个子范围(不同的堆栈帧)中,并没有推断出在definitelyInt范围内具有这种“提升”效果。



2020 年初更新

这是使用getter而不是实例/类方法并覆盖空格isNullisNotNull的修改版本。

第二次更新

它现在也占空列表和地图!

第三次更新

为安全性和模块化/可维护性添加了私有吸气剂。

第四次更新

更新了解决特定问题的答案Map,它在为空时无法正确处理!因为Map不是Iterable. 通过引入_isMapObjectEmptyU解决了这个问题

解决方案

extension TextUtilsStringExtension on String {
  /// Returns true if string is:
  /// - null
  /// - empty
  /// - whitespace string.
  ///
  /// Characters considered "whitespace" are listed [here](https://stackoverflow.com/a/59826129/10830091).
  bool get isNullEmptyOrWhitespace =>
      this == null || this.isEmpty || this.trim().isEmpty;
}

/// - [isNullOrEmpty], [isNullEmptyOrFalse], [isNullEmptyZeroOrFalse] are from [this StackOverflow answer](https://stackoverflow.com/a/59826129/10830091)
extension GeneralUtilsObjectExtension on Object {
  /// Returns true if object is:
  /// - null `Object`
  bool get isNull => this == null;

  /// Returns true if object is NOT:
  /// - null `Object`
  bool get isNotNull => this != null;

  /// Returns true if object is:
  /// - null `Object`
  /// - empty `String`s
  /// - empty `Iterable` (list, set, ...)
  /// - empty `Map`
  bool get isNullOrEmpty =>
      isNull ||
      _isStringObjectEmpty ||
      _isIterableObjectEmpty ||
      _isMapObjectEmpty;

  /// Returns true if object is:
  /// - null `Object`
  /// - empty `String`
  /// - empty `Iterable` (list, map, set, ...)
  /// - false `bool`
  bool get isNullEmptyOrFalse =>
      isNull ||
      _isStringObjectEmpty ||
      _isIterableObjectEmpty ||
      _isMapObjectEmpty ||
      _isBoolObjectFalse;

  /// Returns true if object is:
  /// - null `Object`
  /// - empty `String`
  /// - empty `Iterable` (list, map, set, ...)
  /// - false `bool`
  /// - zero `num`
  bool get isNullEmptyFalseOrZero =>
      isNull ||
      _isStringObjectEmpty ||
      _isIterableObjectEmpty ||
      _isMapObjectEmpty ||
      _isBoolObjectFalse ||
      _isNumObjectZero;

  // ------- PRIVATE EXTENSION HELPERS -------
  /// **Private helper**
  ///
  /// If `String` object, return String's method `isEmpty`
  ///
  /// Otherwise return `false` to not affect logical-OR expression. As `false` denotes undefined or N/A since object is not `String`
  bool get _isStringObjectEmpty =>
      (this is String) ? (this as String).isEmpty : false;

  /// **Private helper**
  ///
  /// If `Iterable` object, return Iterable's method `isEmpty`
  ///
  /// Otherwise return `false` to not affect logical-OR expression. As `false` denotes undefined or N/A since object is not `Iterable`
  bool get _isIterableObjectEmpty =>
      (this is Iterable) ? (this as Iterable).isEmpty : false;

  /// **Private helper**
  ///
  /// If `Map` object, return Map's method `isEmpty`
  ///
  /// Otherwise return `false` to not affect logical-OR expression. As `false` denotes undefined or N/A since object is not `Map`
  bool get _isMapObjectEmpty => (this is Map) ? (this as Map).isEmpty : false;

  /// **Private helper**
  ///
  /// If `bool` object, return `isFalse` expression
  ///
  /// Otherwise return `false` to not affect logical-OR expression. As `false` denotes undefined or N/A since object is not `bool`
  bool get _isBoolObjectFalse =>
      (this is bool) ? (this as bool) == false : false;

  /// **Private helper**
  ///
  /// If `num` object, return `isZero` expression
  ///
  /// Otherwise return `false` to not affect logical-OR expression. As `false` denotes undefined or N/A since object is not `num`
  bool get _isNumObjectZero => (this is num) ? (this as num) == 0 : false;
}
Run Code Online (Sandbox Code Playgroud)

假设

这假定 Dart 2.7 或更高版本支持扩展方法

用法

以上是两个扩展类。一为Object一为String。将它们单独/一起放在一个文件中,并在调用站点文件中导入文件/文件。

描述

来自 Swift,我倾向于使用像用户@Benjamin Menrad 的回答这样的扩展,但在适用时使用 getter 而不是方法/函数——镜像 Dart 的计算属性,如String.isEmpty.

来自 C#,我喜欢他们的 String 的辅助方法IsNullOrWhiteSpace

我的版本合并了上述两个概念。

命名

随意重命名扩展类。我倾向于这样命名它们:

XY扩展

在哪里:

  • X是文件/作者/应用程序/唯一名称。
  • Y是要扩展的类型的名称。

名称示例:

  • MyAppIntExtension
  • DartDoubleExtension
  • TextUtilsStringExtension
  • OHProviderExtension

批评

为什么使用私有 getter 而不是简单的 this == null || this == '' || this == [] || this == 0 || !this

  • 我认为这更安全,因为它首先将对象转换为我们想要比较其值的正确类型。
  • 更加模块化,因为更改是私有 getter 的核心,并且任何修改都反映在任何地方。
  • 如果私有 getter 中的类型检查失败,我们将返回 false,这不会影响根逻辑 OR 表达式的结果。


Har*_*sen 19

你可以做到

if (["", null, false, 0].contains(routeinfo["no_route"])) {
  // do sth
}
Run Code Online (Sandbox Code Playgroud)


lrn*_*lrn 15

我会编写一个辅助函数而不是内联的所有东西.

bool isNullEmptyOrFalse(Object o) =>
  o == null || false == o || "" == o;

bool isNullEmptyFalseOrZero(Object o) =>
  o == null || false == o || 0 == o || "" == o;
Run Code Online (Sandbox Code Playgroud)

这避免了重复查找(如contains操作),但它更具可读性.它也不会List为每个检查创建一个新的文字(使列表const可以修复它).

if (isNullEmptyOrFalse(routeinfo["no_route"])) { ... }
Run Code Online (Sandbox Code Playgroud)

在努力做出简短易读的东西时,制作一个名字很好的辅助函数通常是最好的解决方案.


Ben*_*rad 13

由于来自 Android 和 Kotlin,我更喜欢扩展方法而不是静态辅助方法。在 Dart 2.7 中,您现在也可以使用它:

extension Extension on Object {
  bool isNullOrEmpty() => this == null || this == '';

  bool isNullEmptyOrFalse() => this == null || this == '' || !this;

  bool isNullEmptyZeroOrFalse() =>
      this == null || this == '' || !this || this == 0;
}
Run Code Online (Sandbox Code Playgroud)

现在你可以在代码的任何地方调用这些方法:

if (anyVariable.isNullOrEmpty()) {
  // do something here
}
Run Code Online (Sandbox Code Playgroud)

您可能需要手动导入放置扩展方法的 dart 类,例如:

import 'package:sampleproject/utils/extensions.dart';
Run Code Online (Sandbox Code Playgroud)

  • 启用“null safety”(在 Dart 2.12 beta 中)后,在可为 null 的对象上使用建议的扩展方法之一(例如 `isNullOrEmpty`)会导致错误:`值可以为 'null' 的表达式必须为 null-在取消引用之前进行检查。`。 (2认同)

Cop*_*oad 5

具有空安全性:

假设您有一个可为空的对象Map和一个List其中包含可为空值的对象。

Map<String, List?>? map;
List<String?>? list;
Run Code Online (Sandbox Code Playgroud)

要检查集合是否既不为null空也不为空,您可以执行以下操作:

if (map?.containsKey('foo') ?? false) {
  print('map is not-null, and key "foo" is present.');
}

if (list?.isNotEmpty ?? false) {
  print('list is not-null and not empty');
} 
Run Code Online (Sandbox Code Playgroud)