Dart“ StreamTransformer <rs,rt =“”> cast <rs,rt =“”>()”做什么?

时间:2018-11-19 10:43:16

标签: stream dart

我已经实现了流转换器。请注意,这只是一种练习(为了学习Dart)。该转换器将整数转换为字符串。我在下面提供了代码,您也可以在GitHub上找到它。

// Conceptually, a transformer is simply a function from Stream to Stream that
// is encapsulated into a class.
//
// A transformer is made of:
// - A stream controller. The controller provides the "output" stream that will
//   receive the transformed values.
// - A "bind()" method. This method is called by the "input" stream "transform"
//   method (inputStream.transform(<the stream transformer>).

import 'dart:async';

/// This class defines the implementation of a class that emulates a function
/// that converts a data with a given type (S) into a data with another type (T).
abstract class TypeCaster<S, T> {
  T call(S value);
}

/// This class emulates a converter from integers to strings.
class Caster extends TypeCaster<int, String> {
  String call(int value) {
    return "<${value.toString()}>";
  }
}

// StreamTransformer<S, T> is an abstract class. The functions listed below must
// be implemented:
// - Stream<T> bind(Stream<S> stream)
// - StreamTransformer<RS, RT> cast<RS, RT>()

class CasterTransformer<S, T> implements StreamTransformer<S, T> {

  StreamController<T> _controller;
  bool _cancelOnError;
  TypeCaster<S, T> _caster;

  // Original (or input) stream.
  Stream<S> _stream;

  // The stream subscription returned by the call to the function "listen", of
  // the original (input) stream (_stream.listen(...)).
  StreamSubscription<S> _subscription;

  /// Constructor that creates a unicast stream.
  /// [caster] An instance of "type caster".
  CasterTransformer(TypeCaster<S, T> caster, {
    bool sync: false,
    bool cancelOnError: true
  }) {
    _controller = new StreamController<T>(
        onListen: _onListen,
        onCancel: _onCancel,
        onPause: () => _subscription.pause(),
        onResume: () => _subscription.resume(),
        sync: sync
    );
    _cancelOnError = cancelOnError;
    _caster = caster;
  }

  /// Constructor that creates a broadcast stream.
  /// [caster] An instance of "type caster".
  CasterTransformer.broadcast(TypeCaster<S, T> caster, {
    bool sync: false,
    bool cancelOnError: true
  }) {
      _cancelOnError = cancelOnError;
      _controller = new StreamController<T>.broadcast(
          onListen: _onListen,
          onCancel: _onCancel,
          sync: sync
      );
      _caster = caster;
  }

  /// Handler executed whenever a listener subscribes to the controller's stream.
  /// Note: when the transformer is applied to the original stream, through call
  ///       to the method "transform", the method "bind()" is called behind the
  ///       scenes. The method "bind()" returns the controller stream.
  ///       When a listener is applied to the controller stream, then this function
  ///       (that is "_onListen()") will be executed. This function will set the
  ///       handler ("_onData") that will be executed each time a value appears
  ///       in the original stream. This handler takes the incoming value, casts
  ///       it, and inject it to the (controller) output stream.
  /// Note: this method is called only once. On the other hand, the method "_onData"
  ///       is called as many times as there are values to transform.
  void _onListen() {
    _subscription = _stream.listen(
        _onData,
        onError: _controller.addError,
        onDone: _controller.close,
        cancelOnError: _cancelOnError
    );
  }

  /// Handler executed whenever the subscription to the controller's stream is cancelled.
  void _onCancel() {
    _subscription.cancel();
    _subscription = null;
  }

  /// Handler executed whenever data comes from the original (input) stream.
  /// Please note that the transformation takes place here.
  /// Note: this method is called as many times as there are values to transform.
  void _onData(S data) {
    _controller.add(_caster(data));
  }

  /// This method is called once, when the stream transformer is assigned to the
  /// original (input) stream. It returns the stream provided by the controller.
  /// Note: here, you can see that the process transforms a value of type
  ///       S into a value of type T. Thus, it is necessary to provide a function
  ///       that performs the conversion from type S to type T.
  /// Note: the returned stream may accept only one, or more than one, listener.
  ///       This depends on the method called to instantiate the transformer.
  ///       * CasterTransformer() => only one listener.
  ///       * CasterTransformer.broadcast() => one or more listener.
  Stream<T> bind(Stream<S> stream) {
    _stream = stream;
    return _controller.stream;
  }

  // TODO: what should this method do ? Find the answer.
  StreamTransformer<RS, RT> cast<RS, RT>() {
    return StreamTransformer<RS, RT>((Stream<RS> stream, bool b) {
      // What should we do here ?
    });
  }
}


main() {

  // ---------------------------------------------------------------------------
  // TEST: unicast controller.
  // ---------------------------------------------------------------------------

  // Create a controller that will be used to inject integers into the "input"
  // stream.
  StreamController<int> controller_unicast = new StreamController<int>();
  // Get the stream "to control".
  Stream<int> integer_stream_unicast = controller_unicast.stream;
  // Apply a transformer on the "input" stream.
  // The method "transform" calls the method "bind", which returns the stream that
  // receives the transformed values.
  Stream<String> string_stream_unicast = integer_stream_unicast.transform(CasterTransformer<int, String>(new Caster()));

  string_stream_unicast.listen((data) {
    print('String => $data');
  });

  // Inject integers into the "input" stream.
  controller_unicast.add(1);
  controller_unicast.add(2);
  controller_unicast.add(3);

  // ---------------------------------------------------------------------------
  // TEST: broadcast controller.
  // ---------------------------------------------------------------------------

  StreamController<int> controller_broadcast = new StreamController<int>.broadcast();
  Stream<int> integer_stream_broadcast = controller_broadcast.stream;
  Stream<String> string_stream_broadcast = integer_stream_broadcast.transform(CasterTransformer<int, String>.broadcast(new Caster()));

  string_stream_broadcast.listen((data) {
    print('Listener 1: String => $data');
  });

  string_stream_broadcast.listen((data) {
    print('Listener 2: String => $data');
  });

  controller_broadcast.add(1);
  controller_broadcast.add(2);
  controller_broadcast.add(3);
}

CasterTransformer<S, T>扩展了抽象类StreamTransformer<S, T>

因此,它实现了方法StreamTransformer<RS, RT> cast<RS, RT>()

在文档中,据说:

  

生成的转换器将在运行时检查其转换的流的所有数据事件实际上是S的实例,并且将检查由该转换器产生的所有数据事件实际上是RT的实例。

请参阅:https://api.dartlang.org/stable/2.1.0/dart-async/StreamTransformer/cast.html

首先,我认为本文档中有一个错别字:应该说“ ...它的转换实际上是RS的实例”(而不是S)。

但是,这对我来说似乎很模糊。

  • 为什么我们需要一个流转换器来检查值类型?变压器的目的是进行转换,不是吗?如果组件的目的是检查,那么为什么不将其称为检查器呢?
  • 而且,为什么我们还要检查(我们实现的)转换器产生所需的数据?如果没有,那么我们将面临一个应该修复的错误。

有人可以解释方法Cast()的目的吗?

1 个答案:

答案 0 :(得分:3)

可以使用cast方法来键入操作。

如果您有StreamTransformer<num, int>,它将数字转换为整数(例如,对它们调用.toInt(),然后加42,因为这显然很有用!)。 如果要在需要StreamTransformer<int, num>的某个地方使用该变压器,则不能。由于num不是int的子类型,因此无法将转换器指定给该类型。

但是您知道,因为您了解流转换器的实际工作原理,所以第一个类型参数仅用于输入。只能接受num的地方,可以接受任何int的东西。 因此,要说服类型系统您知道自己在做什么,可以这样写:

StreamTransformer<int, num> transform = myTranformer.cast<int, num>();

现在,tranformer接受任何整数(RS),检查它是否为numS),并将其传递给myTransformer,后者调用{ {1}}加上42,然后将所得的toInt()int)传回,T检查它是否为transformernum),然后发出来。

一切正常,并且类型系统很满意。

您可以使用RT来执行在运行时永远无法执行的操作,因为它所做的只是添加额外的运行时检查,以使静态类型系统确信这些操作将成功或失败。检查。

获得cast实现的最简单方法是使用StreamTransformer.cast静态方法:

StreamTransformer.castFrom

这将在您自己的转换器上使用系统的默认强制转换包装。