forEach 与 for in:调用方法时的不同行为

forEach 与 for in:调用方法时的不同行为

问题描述:

我注意到 forEachfor in 会产生不同的行为.我有一个 RegExp 列表,并希望在每个列表上运行 hasMatch.当使用 forEach 遍历列表时,hasMatch 永远不会返回 true.但是,如果我使用 for inhasMatch 返回 true.

I noticed that forEach and for in to produce different behavior. I have a list of RegExp and want to run hasMatch on each one. When iterating through the list using forEach, hasMatch never returns true. However, if I use for in, hasMatch returns true.

示例代码:

class Foo {
  final str = "Hello";
  final regexes = [new RegExp(r"(w+)")];

  String a() {
    regexes.forEach((RegExp reg) {
      if (reg.hasMatch(str)) {
        return 'match';
      }
    });
    return 'no match';
  }

  String b() {
    for (RegExp reg in regexes) {
      if (reg.hasMatch(str)) {
        return 'match';
      }
    }
    return 'no match';
  }
}

void main() {
  Foo foo = new Foo();
  print(foo.a()); // prints "no match"
  print(foo.b()); // prints "match"
}

(DartPad 上面的示例代码)

ab 方法的唯一区别是 a 使用 forEachb 使用 for in,但它们产生不同的结果.这是为什么?

The only difference between the methods a and b is that a uses forEach and b uses for in, yet they produce different results. Why is this?

虽然有一个 prefer_foreach lint,该建议专门用于可以将其与撕下(对现有函数的引用)一起使用的情况.有效的 Dart 推荐 反对使用Iterable.forEach和其他任何东西,并且有相应的avoid_function_literals_in_foreach_calls lint 来强制执行.

Although there is a prefer_foreach lint, that recommendation is specifically for cases where you can use it with a tear-off (a reference to an existing function). Effective Dart recommends against using Iterable.forEach with anything else, and there is a corresponding avoid_function_literals_in_foreach_calls lint to enforce it.

除了回调是撕掉的那些简单情况外,Iterable.forEach 并不比使用基本和更通用的 for 循环更简单.使用 Iterable.forEach 有更多的陷阱,这就是其中之一.

Except for those simple cases where the callback is a tear-off, Iterable.forEach is not any simpler than using a basic and more general for loop. There are more pitfalls using Iterable.forEach, and this is one of them.

  • Iterable.forEach 是一个将 回调 作为参数的函数.Iterable.forEach 不是控制结构,回调是普通函数.因此,您不能使用 break 提前停止迭代或使用 continue 跳到下一次迭代.

  • Iterable.forEach is a function that takes a callback as an argument. Iterable.forEach is not a control structure, and the callback is an ordinary function. You therefore cannot use break to stop iterating early or use continue to skip to the next iteration.

回调中的return语句从回调中返回,返回值被忽略.Iterable.forEach 的调用者永远不会收到返回的值,也永远不会有机会传播它.例如,在:

A return statement in the callback returns from the callback, and the return value is ignored. The caller of Iterable.forEach will never receive the returned value and will never have an opportunity to propagate it. For example, in:

bool f(List<int> list) {
  for (var i in list) {
    if (i == 42) {
      return true;
    }
  }
  return false;
}

return true 语句从函数 f 返回并停止迭代.相比之下,使用 forEach:

the return true statement returns from the function f and stops iteration. In contrast, with forEach:

bool g(List<int> list) {
  list.forEach((i) {
    if (i == 42) {
      return true;
    }
  });
  return false;
}

return true 语句仅从回调中返回.函数g 直到它完成all 次迭代并到达最后的return false 语句时才会返回.这也许更清楚:

the return true statement returns from only the callback. The function g will not return until it completes all iterations and reaches the return false statement at the end. This perhaps is clearer as:

bool callback(int i) {
  if (i == 42) {
    return true;
  }
}

bool g(List<int> list) {
  list.forEach(callback);
  return false;
}

更明显的是:

  1. callback 无法使 g 返回 true.
  2. callback 不会沿所有路径返回值.
  1. There is no way for callback to cause g to return true.
  2. callback does not return a value along all paths.

(这就是你遇到的问题.)

(That's the problem you encountered.)

Iterable.forEach 不得与异步回调一起使用.由于回调返回的任何值都被忽略,因此永远无法等待异步回调.

Iterable.forEach must not be used with asynchronous callbacks. Because any value returned by the callback is ignored, asynchronous callbacks can never be waited upon.

我还应该指出,如果您启用 Dart 的新空安全功能,它启用更严格的类型检查,您的 forEach 代码将生成一个错误,因为它在回调中返回一个值预期有一个 void 返回值.

I should also point out that if you enable Dart's new null-safety features, which enable stricter type-checking, your forEach code will generate an error because it returns a value in a callback that is expected to have a void return value.