在 Dart 中使用泛型类型参数进行回调

Callback with generic type parameter in Dart

我正在尝试定义一个回调函数,它需要接受一个通用参数和 return 一个相同类型的值。请记住,以下示例是我实际需要的过于简化的版本。

final T Function<T>(T value) self = (value) => value

这会导致以下错误,我似乎无法摆脱它。

The argument type '(dynamic) → dynamic' can't be assigned to the parameter type '<T>(T) → T'

dart(argument_type_not_assignable)

似乎唯一可行的方法是为值指定类型,但这首先违背了使用类型参数的目的。

final T Function<T>(T value) same = <String>(value) => value;

我需要它是通用的,以便调用者可以在 return 中传递它期望的类型。我还需要将它存储在一个变量中,这样我就可以将它作为回调函数传递。

如果这不是直接可行的,您知道任何解决方法吗?提前谢谢你。


如果要求不明确,这里有一个更完整的示例。

abstract class Provider<T> {
  T get bar;
}

class StringProvider extends Provider<String> {
  String get bar => 'bar';
}

class NumberProvider extends Provider<int> {
  int get bar => 42;
}

class Foo {
  final T Function<T>(Provider<T> provider) provide;

  const Foo({ this.provide });
}

test() {
  final foo = Foo(provide: (provider) => provider.bar);

  String strValue = foo.provide(StringProvider()); // should be 'bar'
  int numValue = foo.provide(NumberProvider()); // should be 42
}

烦人的是 Dart 实际上理解 foo.provide(StringProvider()) 将 return 一个字符串,而使用 NumberProvider 确实 return 一个整数,然而,错误对于实际给变量赋值的那一行,还是上升了。

final foo = Foo(provide: (provider) => provider.bar);
The argument type '(dynamic) → dynamic' can't be assigned to the parameter type '<T>(Provider<T>) → T'

您必须定义一个独立的泛型函数:

class Foo {
  final T Function<T>(Provider<T> provider) provide;

  const Foo({ this.provide });
}

main() {
  T f<T>(Provider<T> provider) => provider.bar;
  final foo = Foo(provide: f);

  String strValue = foo.provide(StringProvider()); // should be 'bar'
  int numValue = foo.provide(NumberProvider()); // should be 42
}

事实证明,我可以通过在定义值时提供任何具体类型来欺骗类型检查器。请注意,dynamic 是不允许的,但其他任何东西都可以。

final foo = Foo(provide: <int>(provider) => provider.bar);

这既消除了错误,又允许 provide 方法在调用时 return 正确的类型。

总之,这似乎是类型检查器的一个简单缺点,而不是使用现有语言功能实际上不可能或难以实现的缺点。我将在该语言的 GitHub 存储库中提出一个问题,以便进行进一步的调查和讨论。


更新 #1:问题已在 GitHub 上打开。


更新#2:问题已解决,事实证明该行为是设计使然。引用 SDK 团队的 Erik Ernst

Try this: final foo = Foo(provide: <T>(Provider<T> provider) => provider.bar);!

The problem is that you're passing a non-generic function to the Foo constructor, and you should pass a generic function. There is no subtype relationship between a generic function type and a non-generic function type, so according to the type checker you might as well pass a String, and that's the reason for the 'can't be assigned to' message.

事实证明,只需在参数列表前添加 <T>(而不是原始解决方法中的 int)即可解决问题。

final foo = Foo(provide: <T>(provider) => provider.bar);

这迫使 Dart 理解提供者是 Provider<T> 类型,并且该方法将 return 类型 T 的值,使我们免于使用具体类型并仍然得到消除错误。