【问题标题】:Dart What does "StreamTransformer<RS, RT> cast<RS, RT>()" do?Dart “StreamTransformer<RS, RT> cast<RS, RT>()”是做什么的?
【发布时间】:2019-04-21 17:02:52
【问题描述】:

我已经实现了流转换器。请注意,这只是一个练习(为了学习 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&lt;S, T&gt; 类扩展了抽象类StreamTransformer&lt;S, T&gt;

因此,它实现了方法StreamTransformer&lt;RS, RT&gt; cast&lt;RS, RT&gt;()

文档上说:

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

见:https://api.dartlang.org/stable/2.1.0/dart-async/StreamTransformer/cast.html

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

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

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

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

【问题讨论】:

    标签: stream dart


    【解决方案1】:

    cast 方法可以帮助键入操作。

    如果您有StreamTransformer&lt;num, int&gt;,它会将数字转换为整数(例如,通过在它们上调用.toInt(),然后添加42,因为这显然很有用!)。 如果您想在某个需要StreamTransformer&lt;int, num&gt; 的地方使用该转换器,那么您不能。由于num 不是int 的子类型,因此转换器不能分配给该类型。

    但是您知道,因为您了解流转换器的实际工作原理,所以第一个类型参数仅用于输入。接受任何num 的东西应该可以安全地使用,只要它只给出ints。 所以,为了让类型系统相信你知道你在做什么,你写:

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

    现在,tranformer 接受任意整数 (RS),检查它是否为 num (S),然后将其传递给调用 toInt()myTransformer 并添加 42,然后得到 @ 987654337@ (T) 被传回,transformer 检查它是否是 num (RT) 并发出。

    一切正常并且类型系统很开心。

    你可以使用cast 来做一些在运行时永远不会工作的事情,因为它所做的只是添加额外的运行时检查,让静态类型系统相信事情要么成功要么抛出这些检查。

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

    StreamTransformer<RS, RT> cast<RS, RT>() => StreamTransformer.castFrom(this);
    

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

    【讨论】:

    • 感谢您的回复。我知道该方法只是实例化一个与所需输入/输出数据类型兼容的新转换器。我更新了my example 来说明这一点。
    • 啊,是的。添加了有关如何创建新包装的变压器的说明。
    • 感谢您的来信。事实上, castFrom(this) 很方便。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-22
    • 2011-06-27
    • 1970-01-01
    • 2021-08-21
    • 1970-01-01
    • 2013-11-11
    • 1970-01-01
    相关资源
    最近更新 更多