向 Dart 中的函数添加带有泛型参数的 void 回调

Adding a void callback with a generic parameter to a function in Dart

我想做什么

给定以下节点:

class Node<T> {
  Node(this.value);
  T value;
  Node? child;

  // TODO: add `visit` method

  @override
  String toString() => value.toString();
}

我想添加一个 visit 方法,该方法将递归地对每个节点及其子节点的值执行一些操作。然后我可以做这样的事情:

void main() {
  final root = Node(1);
  root.child = Node(2);
  root.child!.child = Node(3);

  // one of these
  root.visit(print);
  root.visit((value) => print(value));

  // 1
  // 2
  // 3
}

天真的解决方案

如果我执行以下操作,它会起作用:

void visit(Function action) {
  action(value);
  child?.visit(action);
}

天真的解决方案存在问题

然而,这个语句中的value被推断为dynamic:

root.visit((value) => print(value));

我想将其推断为与节点的通用 T 类型相同的类型。

此外,编译器允许以下操作,这会导致 运行 时间崩溃:

root.visit(() => 42);

我希望这是一个编译时错误。

尝试的解决方案 1

如果我将 visit 更改为以下内容:

void visit(Function(T value) action) {
  action(value);
  child?.visit(action(value));
}

编译时一切看起来都很好:

root.visit(print);                    // OK
root.visit((value) => print(value));  // OK
root.visit(() => 42);                 // error

但是如果我注释掉最后一个和 运行 前两个代码中的任何一个,那么我将得到以下 运行 时间错误:

Unhandled exception:
type 'Null' is not a subtype of type '(dynamic) => dynamic'

我不太清楚那是什么意思。

尝试的解决方案 2

已添加 void:

void visit(void Function(T value) action) {
  action(value);
  child?.visit(action(value)); // error
}

This expression has a type of 'void' so its value can't be used. Try checking to see if you're using the correct API; there might be a function or call that returns void you didn't expect. Also check type parameters and variables which might also be void. (dartuse_of_void_result)

尝试的解决方案 3

这只是暗中刺杀:

void visit(void Function<T>(T value) action) {
  action(value);
  child?.visit(action); 
}

visit 方法似乎可以编译,但像以前一样调用它会出现编译时错误:

root.visit(print);                    // error
root.visit((value) => print(value));  // error

错误读取:

The argument type 'void Function(Object?)' can't be assigned to the parameter type 'void Function(T)'. (dartargument_type_not_assignable)

相关问题

这些问题似乎相关,但我不知道如何从中提取解决方案:

我该如何解决这个问题?

感谢@jamesdlin 在评论中解决问题。

您需要将 child 的通用类型设置为 Node<T>。然后您可以将方法签名指定为 void visit(Function(T value) action) 并将 action 本身传递给 child.

完整示例如下:

void main() {
  final root = Node(1);
  root.child = Node(2);
  root.child!.child = Node(3);

  // one of these
  root.visit(print);
  root.visit((value) => print(value)); // value inferred as int
  // root.visit(() => 42); // compile-time error

  // 1
  // 2
  // 3
}

class Node<T> {
  Node(this.value);
  T value;
  Node<T>? child;

  void visit(Function(T value) action) {
    action(value);
    child?.visit(action);
  }

  @override
  String toString() => value.toString();
}