带有泛型的Dart typedef导致子类型错误

g52tjvyc  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(100)

注意:我已经完全重写了这个问题,因为我知道有一段完整的代码可以重现这个问题。
下面的代码失败:

Unhandled exception:
type '(OnBadAir) => bool' is not a subtype of type '(Event) => bool'
#0      Fork._dispatch
typedef.dart:33
#1      main

这是从fsm 2包中提取的。
因为'dispatch'事件正在处理一个具有不同事件类型的事件队列,所以dispatch事件不能被类型化,然后我们遇到了typedef是逆变的问题(如@Lrn所指出的)。
因此,问题是如何将“条件”传递到调度程序可以处理的事件队列中?

import 'dart:async';

class Event {}

class OnBadAir extends Event {
  OnBadAir(this.quality);
  int quality;
}

typedef GuardCondition<E extends Event> = bool Function(E event);

class _QueuedEvent {
  _QueuedEvent(this.event);
  Event event;
}

class Fork<E extends Event> {
  Fork(this.td);

  TransitionDefinition td;
  final _eventQueue = <_QueuedEvent>[];

  void queue(E event) {
    _eventQueue.add(_QueuedEvent(event));
  }

  /// dequeue the next event and transition it.
  Future<void> _dispatch() async {
    assert(_eventQueue.isNotEmpty, 'The event queue is in an invalid state');
    final event = _eventQueue.first;

    /// crashes here.
    if (td.condition(event.event)) {
      print('it worked');
    }
  }
}

class TransitionDefinition<E extends Event> {
  TransitionDefinition({required this.condition});
  final GuardCondition<E> condition;
}

void main() async {
  final fork = Fork<OnBadAir>(
      TransitionDefinition<OnBadAir>(condition: (e) => e.quality < 10))
    ..queue(OnBadAir(10));

  await fork._dispatch();
}
cbjzeqam

cbjzeqam1#

我的猜测是你已经声明了TransitionDefinition如下:

class TransitionDefinition<E extends Event> {
  final GuardDefinition<E> condition;
  // ...
}

td实际上是一个TransitionDefinition<OnBadAir>类型的示例。
但是,由于某种原因,_evaluateConditions(td, OnBadAir(5));<Event>的类型参数推断为_evaluateConditions,因此_evaluateConditions中的td具有TransitionDefinition<Event>的静态类型(TransitionDefinition<E>,其中E动态绑定到Event)。
这意味着当你得到表达式td.condition时,代码需要一个bool Function(Event)。由于类的类型参数在condition中出现 contravariantly,但类泛型是协变的,因此编译器不能假设运行时值是bool Function(Event)。所以它插入一个检查。
检查失败,因为实际类型是bool Function(OnBadAir),而不是bool Function(Event)。前者不能用与后者相同的参数调用,这是我们对子类型的要求的一部分。这就是为什么你得到一个运行时错误。
如果您可以使用_evaluateConditions<OnBadAir>(td, OnBadAir(5));来执行调用,则错误可能会消失。
一般来说,在函数类型的字段或返回类型中使用类的类型参数作为 parameter(在逆变位置)类型是不安全的。如果将这样的类示例向上强制转换为超类型,并尝试访问该字段,则可能会引发。为了您自己的保护,由于该值不是合理的,因此它的类型不会是该表达式的静态预期类型的子类型。

相关问题