|
using System; |
|
using System.Collections.Generic; |
|
using System.Threading; |
|
using System.Threading.Tasks; |
|
|
|
namespace IanSavchenko.Tools |
|
{ |
|
/// <summary> |
|
/// Represents a source for monitorable object - something that has a value and can change later |
|
/// </summary> |
|
public class MonitorableSource<T> |
|
{ |
|
private T _value; |
|
|
|
public MonitorableSource() |
|
{ |
|
Monitorable = new Monitorable<T>(this); |
|
} |
|
|
|
public Monitorable<T> Monitorable { get; } |
|
|
|
public T Value |
|
{ |
|
get { return _value; } |
|
set |
|
{ |
|
var prevValue = _value; |
|
_value = value; |
|
|
|
if (!EqualityComparer<T>.Default.Equals(prevValue, value)) |
|
Updated?.Invoke(this, new MonitorableUpdatedEventArgs<T>(value)); |
|
} |
|
} |
|
|
|
public event EventHandler<MonitorableUpdatedEventArgs<T>> Updated; |
|
|
|
public static explicit operator MonitorableSource<T>(T value) |
|
{ |
|
return new MonitorableSource<T>() { _value = value }; |
|
} |
|
} |
|
|
|
|
|
/// <summary> |
|
/// Represents a read-only object which value can be accessed and value changed noitifications can be subscribed to |
|
/// </summary> |
|
public class Monitorable<T> |
|
{ |
|
private readonly MonitorableSource<T> _source; |
|
|
|
public Monitorable(MonitorableSource<T> source) |
|
{ |
|
_source = source; |
|
} |
|
|
|
public T Value => _source.Value; |
|
|
|
public event EventHandler<MonitorableUpdatedEventArgs<T>> Updated |
|
{ |
|
add { _source.Updated += value; } |
|
remove { _source.Updated -= value; } |
|
} |
|
} |
|
|
|
|
|
/// <inheritdoc /> |
|
/// <summary> |
|
/// Event arguments for monitorable updated |
|
/// </summary> |
|
/// <typeparam name="T"></typeparam> |
|
public class MonitorableUpdatedEventArgs<T> : EventArgs |
|
{ |
|
public MonitorableUpdatedEventArgs(T updatedValue) |
|
{ |
|
UpdatedValue = updatedValue; |
|
} |
|
|
|
public T UpdatedValue { get; } |
|
} |
|
|
|
|
|
/// <summary> |
|
/// Handy utils for monitorable |
|
/// </summary> |
|
public static class MonitorableExtensions |
|
{ |
|
/// <summary> |
|
/// Asynchronously waits for <paramref name="monitorable"/> to get a <paramref name="value"/> |
|
/// </summary> |
|
/// <returns>Returns a Task which is resolved when monitorable gets specified <paramref name="value"/></returns> |
|
public static Task<T> WaitForValue<T>(this Monitorable<T> monitorable, T value, CancellationToken cancellationToken = default(CancellationToken)) |
|
{ |
|
var tcs = new TaskCompletionSource<T>(); |
|
cancellationToken.Register(() => tcs.TrySetCanceled()); |
|
|
|
var updatedDelegate = new EventHandler<MonitorableUpdatedEventArgs<T>>((sender, args) => |
|
{ |
|
if (EqualityComparer<T>.Default.Equals(args.UpdatedValue, value)) |
|
tcs.TrySetResult(value); |
|
}); |
|
|
|
monitorable.Updated += updatedDelegate; |
|
|
|
// when task resolved - unsubscribing |
|
tcs.Task.ContinueWith(t => monitorable.Updated -= updatedDelegate); |
|
|
|
if (EqualityComparer<T>.Default.Equals(monitorable.Value, value)) |
|
tcs.TrySetResult(value); |
|
|
|
return tcs.Task; |
|
} |
|
} |
|
} |