Reactive Extensions를 사용하는 것이 좋습니다. 값의 흐름 (이벤트)을 처리하고 잠금 요구를 제거 할 수 있습니다.
먼저 Add
, Complete
및 RequestView
작업에 대한 몇 가지 작업 클래스를 정의 할 예정입니다.
aggregator
가 EventAction
를 수집합니다 : 그것은 세 가지 수신 Subject
값을 개최한다 public class EventAction
{
public static EventAction Add(int value) => new AddAction(value);
public static readonly RequestViewAction RequestView = new RequestViewAction();
public static readonly EventAction Complete = new CompleteAction();
}
public class AddAction : EventAction
{
public readonly int Value;
public AddAction(int value) => Value = value;
}
public class CompleteAction : EventAction
{
}
public class RequestViewAction : EventAction
{
}
다음으로는 AggregateView
라는 형식을 만들려고 :이 예를 들어 F #에서 차별 노동 조합처럼 행동하는 것입니다 이벤트를 처리하고 집계 된 Lst<int>
(Lst<int>
은 the language-ext functional language extensions library에서 불변 목록 유형이지만 ImmutableList
도 사용할 수 있음).
using System;
using LanguageExt;
using static LanguageExt.Prelude;
using System.Reactive.Linq;
using System.Reactive.Subjects;
public class AggregateView : IDisposable
{
readonly Subject<EventAction> aggregator = new Subject<EventAction>();
readonly Subject<int> events = new Subject<int>();
readonly Subject<Lst<int>> view = new Subject<Lst<int>>();
readonly IDisposable subscription;
public AggregateView()
{
// Creates an aggregate view of the integers that responds to various control
// actions coming through.
subscription = aggregator.Aggregate(
Lst<int>.Empty,
(list, action) =>
{
switch(action)
{
// Adds an item to the aggregate list and passes it on to the
// events Subject
case AddAction add:
events.OnNext(add.Value);
return list.Add(add.Value);
// Clears the list and passes a list onto the views Subject
case CompleteAction complete:
view.OnNext(Lst<int>.Empty);
return Lst<int>.Empty;
// Gets the current aggregate list and passes it onto the
// views Subject
case RequestViewAction req:
view.OnNext(list);
return list;
default:
return list;
}
})
.Subscribe(x => { });
}
/// <summary>
/// Observable stream of integer events
/// </summary>
public IObservable<int> Events =>
events;
/// <summary>
/// Observable stream of list views
/// </summary>
public IObservable<Lst<int>> Views =>
view;
/// <summary>
/// Listener for plugging into an event
/// </summary>
public void Listener(int value) =>
aggregator.OnNext(EventAction.Add(value));
/// <summary>
/// Clears the aggregate view and post it to Views
/// </summary>
public void Complete() =>
aggregator.OnNext(EventAction.Complete);
/// <summary>
/// Requests a the current aggregate view to be pushed through to
/// the Views subscribers
/// </summary>
public void RequestView() =>
aggregator.OnNext(EventAction.RequestView);
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
subscription?.Dispose();
view?.OnCompleted();
events?.OnCompleted();
view?.Dispose();
events?.Dispose();
}
}
그것은 두 IObservable
속성이 있습니다 : 단순히 Lst<int>
보기 여기
의 스트림 될 것 정수 이벤트
views
의 스트림이 될 것입니다
events
는 클래스의
는 또한 몇 가지 유용한 방법이 있습니다 정수 이벤트를 구독 할 수 있습니다 - 당신이 집계 목록
Events
에 가입 할 수있는 :
Listener
-이 무엇을 당신 event
Complete
이렇게하면 집계 목록이 비워져서 View
관측 가능 으로 빈 목록이 전송됩니다.
RequestView
- 이것은 현재 집계 목록을 관찰 가능한 Views
의 모든 가입자에게 보냅니다.
마지막으로 테스트하기 :
class Program
{
static event Action<int> eventTest;
static void Main(string[] args)
{
var aggregate = new AggregateView();
eventTest += aggregate.Listener;
aggregate.Views.Subscribe(ReceiveList);
aggregate.Events.Subscribe(ReceiveValue);
eventTest(1);
eventTest(2);
eventTest(3);
eventTest(4);
eventTest(5);
aggregate.RequestView();
aggregate.Complete();
eventTest(6);
eventTest(7);
eventTest(8);
eventTest(9);
eventTest(10);
aggregate.RequestView();
}
static void ReceiveList(Lst<int> list) =>
Console.WriteLine($"Got list of {list.Count} items: {ListShow(list)}");
static void ReceiveValue(int x) =>
Console.WriteLine(x);
static string ListShow(Lst<int> list) =>
String.Join(", ", list);
}
이 내가 이벤트를 처리 할 때 생각할 수있는 가장 기능적인 방법입니다. Action<int>
은 기본적으로 부작용이 있고 순수하지 않기 때문에 기능적으로 작업하려는 사람은 항상 붉은 색 플래그이어야합니다. 따라서 가능한 한 부작용을 캡슐화하고 다른 모든 것을 순수하게 만들어야합니다.
그런데 모든 유형의 작업을 일반화 할 수 있습니다. 어떤 점에서 훨씬 유용합니다 :
public enum EventActionTag
{
Add,
Complete,
RequestView
}
public class EventAction<T>
{
public readonly EventActionTag Tag;
public static EventAction<T> Add(T value) => new AddAction<T>(value);
public static readonly EventAction<T> RequestView = new RequestViewAction<T>();
public static readonly EventAction<T> Complete = new CompleteAction<T>();
public EventAction(EventActionTag tag) =>
Tag = tag;
}
public class AddAction<T> : EventAction<T>
{
public readonly T Value;
public AddAction(T value) : base(EventActionTag.Add) =>
Value = value;
}
public class CompleteAction<T> : EventAction<T>
{
public CompleteAction() : base(EventActionTag.Complete)
{ }
}
public class RequestViewAction<T> : EventAction<T>
{
public RequestViewAction() : base(EventActionTag.RequestView)
{ }
}
public class AggregateView<T> : IDisposable
{
readonly Subject<EventAction<T>> aggregator = new Subject<EventAction<T>>();
readonly Subject<T> events = new Subject<T>();
readonly Subject<Lst<T>> view = new Subject<Lst<T>>();
readonly IDisposable subscription;
public AggregateView()
{
// Creates an aggregate view of the integers that responds to various control
// actions coming through.
subscription = aggregator.Aggregate(
Lst<T>.Empty,
(list, action) =>
{
switch(action.Tag)
{
// Adds an item to the aggregate list and passes it on to the
// events Subject
case EventActionTag.Add:
var add = (AddAction<T>)action;
events.OnNext(add.Value);
return list.Add(add.Value);
// Clears the list and passes a list onto the views Subject
case EventActionTag.Complete:
view.OnNext(Lst<T>.Empty);
return Lst<T>.Empty;
// Gets the current aggregate list and passes it onto the
// views Subject
case EventActionTag.RequestView:
view.OnNext(list);
return list;
default:
return list;
}
})
.Subscribe(x => { });
}
/// <summary>
/// Observable stream of integer events
/// </summary>
public IObservable<T> Events =>
events;
/// <summary>
/// Observable stream of list views
/// </summary>
public IObservable<Lst<T>> Views =>
view;
/// <summary>
/// Listener for plugging into an event
/// </summary>
public void Listener(T value) =>
aggregator.OnNext(EventAction<T>.Add(value));
/// <summary>
/// Clears the aggregate view and post it to Views
/// </summary>
public void Complete() =>
aggregator.OnNext(EventAction<T>.Complete);
/// <summary>
/// Requests a the current aggregate view to be pushed through to
/// the Views subscribers
/// </summary>
public void RequestView() =>
aggregator.OnNext(EventAction<T>.RequestView);
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
subscription?.Dispose();
view?.OnCompleted();
events?.OnCompleted();
view?.Dispose();
events?.Dispose();
}
}
ConcurrentBag를 사용해보십시오. https://www.dotnetperls.com/concurrentbag – Trey