Дарт Что значит «СтримТрансформербросать()" делать? - PullRequest
0 голосов
/ 19 ноября 2018

Я реализовал преобразователь потока.Обратите внимание, что это всего лишь упражнение (для изучения Дартс).Этот преобразователь преобразует целые числа в строки.Я даю код ниже, и вы также можете найти его на 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 голосов
/ 19 ноября 2018

Метод cast поможет набрать операцию.

Если у вас есть StreamTransformer<num, int>, он преобразует числа в целые числа (скажем, позвонив им .toInt() и добавив 42, потому что это, очевидно, полезно!). Если вы хотите использовать этот трансформатор в каком-то месте, которое ожидает StreamTransformer<int, num>, то вы не можете. Поскольку num не является подтипом int, преобразователь не может быть назначен этому типу.

Но вы знаете, поскольку вы понимаете, как на самом деле работает потоковый преобразователь, аргумент первого типа используется только для входных данных. То, что принимает любой num, должно безопасно использоваться, если ему дано только int s. Итак, чтобы убедить систему типов, что вы знаете, что делаете, вы пишете:

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

Теперь tranformer принимает любое целое число (RS), проверяет, является ли оно num (S), передает его в myTransformer, что вызывает toInt() и добавляет 42, а затем int (T) передается обратно и transformer проверяет, является ли оно num (RT), и выдает его.

Все работает и система типов довольна.

Вы можете использовать cast, чтобы делать вещи, которые никогда не будут работать во время выполнения, потому что все, что он делает, - это добавляет дополнительные проверки во время выполнения, которые убеждают статическую систему типов в том, что что-то будет либо успешным, либо бросит эти проверки.

Самый простой способ получить реализацию StreamTransformer.cast - использовать статический метод StreamTransformer.castFrom:

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

Это будет использовать системную оболочку по умолчанию для вашего собственного преобразователя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...