.Net Reactive Extension надає розробникам набір функцій для реалізації реактивної моделі програмування для .Net-розробників, щоб зробити обробку подій простішою та більш виразною за допомогою декларативних дій. Хоча ключовими основами реактивного масштабування є інтерфейси IObserver і IObservable, як розробнику часто не потрібно реалізовувати ці інтерфейси самостійно. Бібліотека підтримує вбудований тип Subject<T>, який реалізує інтерфейси та підтримує багато функцій.
Теми є основою для різних тем, доступних у бібліотеці, а також є інші теми — <T>ReplaySubject,<T> BehaviorSubject та AsyncSubject<T>. Корисно розуміти суттєві відмінності між ними та як їх використовувати для кращого використання бібліотеки.
У цій статті ми порівняємо<T> Суб'єкта та його родича, намагаючись проілюструвати відмінності в їхній поведінці.
Суб'єкт<T>
Як уже згадувалося, Subject<T> є основою доступних тем, забезпечуючи простий спосіб користуватися бібліотекою без необхідності самостійно реалізовувати інтерфейси IObservable<T> і IObserver<T>. Нижче показано просту демонстрацію типу теми.
У наведеному вище коді ми створили <T>екземпляр Subject, і оскільки він реалізує<T> IObserver та IObserverable<T>, використовуємо той самий екземпляр для підписки та публікації значення в IObserver. Ще один важливий момент — це те, як ми використовуємо перевантаження методу Subscribe для прийняття дій як вхідних даних. Це буде зроблено для кожного опублікованого значення, у цьому випадку друкуючи число на консоль.
Спробуємо показати опубліковані значення та значення, які IObserver (у цій дії<T>) друкує на консоль на наступному зображенні. Це допоможе нам легко порівнювати решту братів і сестер і варіанти.
Перший рядок представляє опубліковане значення, а другий — значення, отримане IObserver. Крім того, ми додали рядок, який вказує, у який момент спостерігач підписується на потік під час виконання. Ця лінія представлена вертикальною пунктирною лінією.
У наведеному вище коді ми помітили, що спостерігач підписався на потік даних до публікації першого значення. Зображення показує рядок Subscriber, розміщений перед першим елементом. Як видно з вихідної лінії, це не впливає на вихід (на цьому етапі).
Але що, якщо спостерігач підписується на дані лише після того, як деякі значення вже опубліковані? Чи впливає це на дані, які отримують спостерігачі? Перш ніж дивитися на результат, давайте спочатку напишемо той самий код.
У наведеному вище коді можна спостерігати, що спостерігач підписується на потік даних лише після публікації двох значень (1 і 2). Як і слід було очікувати, це призведе до того, що спостерігачі не отримуватимуть опубліковані дані до виклику методу підписки. Як показано на рисунку нижче.
А що, якщо ви хочете прочитати всі опубліковані цінності, навіть якщо Observer підписується пізно? Ось тут<T> і вступає в гру ReplaySubject.
ReplayТема<T>
ReplaySubject<T> кешує значення та відтворює їх для наступних підписників. Це корисно для уникнення гоночних умов. Давайте змінимо попередній код на<T> ReplaySubject і подивимось, як це впливає на те, що отримує спостерігач.
Як показано у наведеному вище коді,<T> <T>у коді майже немає змін, окрім того, що ми тепер використовуємо ReplaySubject замість subject. Наступна діаграма ілюструє вплив на отримані спостерігачем дані.
Як показано на зображенні, кешоване значення тепер відтворюється для абонента, навіть якщо абонент підписується пізніше. Звісно, ця корисна функція має свою ціну. Ця реалізація кешує всі значення, опубліковані підписником, що може спричинити проблеми з пам'яттю, якщо обсяг даних значно більший.
Однак ReplaySubject<T> має більше ніж один спосіб розв'язати цю проблему. Для цього прикладу ми розглянемо два приклади, які використовують обмеження розміру та часу для обмеження кешованого значення.
У першому випадку ми використаємо розмір кешу для обмеження його значення. <T>Конструктор ReplaySubject забезпечує перевантаження, яке приймає ціле число, що відображає розмір кеш-буфера (максимальна кількість елементів). У нашому прикладі змінимо код, щоб обмежити розмір кешу до 1.
Зверніть увагу, як ми <T>використовуємо перевантаження конструктора ReplaySubject, щоб визначити розмір кешу як 1. Це обмежує кешування і гарантує, що лише один елемент кешується і замінюється новим одразу після публікації. Вплив цієї зміни показано нижче.
Ще один спосіб обмежити кешування — обмежити час кешованого елемента, іншими словами, забезпечити термін придатності для цього предмета.
Давайте напишемо код, щоб проілюструвати цей приклад.
Подібно до попереднього коду, ми<T> використовуємо перевантаження конструктора ReplaySubject для визначення терміну придатності предметів у кеші. Щоб продемонструвати нашу ситуацію, ми ввели затримку між випуском значень.
Оскільки для підписки спостерігача потрібно повних 1200 мс, будь-які елементи, що перевищують термін придатності 1000 мс, будуть видалені з кешу. У цьому прикладі це призведе до видалення значення 1 із кешу і не буде відтворюватися для пізніх підписників. Як показано на рисунку нижче.
Існують <T>й інші перевантаження ReplaySubject, які дають більше гнучкості та тонко налаштовують кешовані значення, але, наприклад, ми залишимо вже згадані вище два приклади.
ПоведінкаТема<T>
BehaviourSubject <T>дуже схожий на<T> ReplaySubject тим, що допомагає кешувати значення. Але є суттєва різниця. BehaviourSubject<T> кешує лише останнє опубліковане значення. Перш ніж ми заглибимося в це, давайте напишемо трохи коду.
Якщо BehaviorSubject<T> кешує лише одне значення (яке останнє відоме), чим він відрізняється від ReplaySubject розміру 1<T>? Наступна діаграма чітко відображає ситуацію наведеного вище коду.
Однак це не зовсім так. Тут потрібно розуміти дві важливі відмінності. Перша — наявність дефолтів. Зверніть увагу, що в наведеному вище <T>коді ми надаємо значення 0 як стандартне в конструкторі BehaviourSubject. Якщо в кеші немає значення (або іншими словами, дані не були опубліковані до підписки спостерігача), значення за замовчуванням повернеться. Це відрізняється від ReplaySubject, який має розмір 1<T>, що не має жодної цінності. Наступний код і візуальне представлення послідовності демонструють цю поведінку.
Друга відмінність — це поведінка<T> BehaviorSubject і<T> ReplaySubject під час підписки на завершену послідовність. Після підписки після завершення BehaviourSubject <T> не матиме значення, як показано в коді нижче.
Підписники гарантовано не отримають жодної цінності, оскільки підписки відбуваються після завершення.
Однак <T>це стосується і ReplaySubject. Немає гарантії, що спостерігач не отримає жодних значень, як показано в коді нижче.
Як показано в наведеному вище коді, кеш має розмір 1, і навіть якщо підписка буде викликана після завершення виклику, кеш залишиться (доки не буде виконано умову закінчення), тому в цьому випадку буде отримано останнє опубліковане значення.
AsyncSubject<T>
<T>AsyncSubject — це останній «сестра» Суб'єкта, якого ми розглянемо в цій <T>статті, і він дуже схожий на попередні два (ReplaySubject та BehaviourSubject) тим, що також кешує результати. Але знову ж таки, є суттєва різниця. AsyncSubject публікує останнє кешоване значення лише якщо послідовність позначена як <T> завершена (він кешує лише одне значення — останнє значення).
Розглянемо наступний код.
Це створить для спостерігача значення, що послідовність позначається як останнє опубліковане перед завершенням — значення 4. Як показано на рисунку нижче.
Але що станеться, якщо пропустити виклик, який позначає послідовність як завершену? Давайте прокоментуємо з цього рядка і спробуємо ще раз.
Це не генерує жодних даних для спостерігача, оскільки AsyncSubject<T> публікує результати лише після того, як послідовність позначена як повна.
Це суттєва різниця, яку варто пам'ятати кожен, хто користується AsyncSubject<T>.
висновок
У цій статті <T>демонструються відмінності між різними «братами і сестрами» Суб'єкта та деякі його варіації. Часто корисно усвідомлювати ці тонкі відмінності, адже вони можуть проявити іншу поведінку, ніж ви очікували, якщо ви цього не усвідомлюєте.
Оригінальне посилання:Вхід за гіперпосиланням видно.
|