Skip to content

Instantly share code, notes, and snippets.

@IanSavchenko
Last active January 1, 2018 19:35
Show Gist options
  • Select an option

  • Save IanSavchenko/d32ec4024f0b1a97e33874ff5d4c214c to your computer and use it in GitHub Desktop.

Select an option

Save IanSavchenko/d32ec4024f0b1a97e33874ff5d4c214c to your computer and use it in GitHub Desktop.
Monitorable C#
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;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment