Last active
January 1, 2018 19:35
-
-
Save IanSavchenko/d32ec4024f0b1a97e33874ff5d4c214c to your computer and use it in GitHub Desktop.
Revisions
-
IanSavchenko revised this gist
Jan 1, 2018 . 3 changed files with 30 additions and 28 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -32,9 +32,11 @@ can be rewritten like this: ```cs public class MyBetterClass { // Exposes getter-setter property _property private readonly MonitorableSource<int> _property = new MonitorableSource<int>(); // Exposes getter-only property Property.Value and event Property.Updated public Monitorable<int> Property => _property.Monitorable; } ``` This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,38 +1,36 @@ public class MonitorableSourceExample { private readonly MonitorableSource<int> _a; private readonly MonitorableSource<int> _b; private readonly MonitorableSource<int> _c; public MonitorableSourceExample() { _a = new MonitorableSource<int>(); _b = new MonitorableSource<int>(); _c = new MonitorableSource<int>(); } public Monitorable<int> A => _a.Monitorable; public Monitorable<int> B => _b.Monitorable; public Monitorable<int> C => _c.Monitorable; } public class MonitorableUsingExample { private readonly MonitorableSourceExample _something; public MonitorableUsingExample() { _something = new MonitorableSourceExample(); _something.A.Updated += (sender, args) => { // do something on A changing }; } public int ADouble => _something.A.Value * 2; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -40,6 +40,7 @@ public static explicit operator MonitorableSource<T>(T value) } } /// <summary> /// Represents a read-only object which value can be accessed and value changed noitifications can be subscribed to /// </summary> @@ -77,6 +78,7 @@ public MonitorableUpdatedEventArgs(T updatedValue) public T UpdatedValue { get; } } /// <summary> /// Handy utils for monitorable /// </summary> -
IanSavchenko revised this gist
Jan 1, 2018 . 2 changed files with 79 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,41 @@ # Monitorable for C# Sometimes (pretty often) there is a need to have a class with properties which can have value and a coupled event for when this value changes. Monitorable allows to skip a routine of creating a readonly property with an event. Repeating code like this: ```cs class MyClass { private int _property = 0; public int Property { get { return _property; } private set { if (_property == value) return; _property = value; PropertyUpdated(this, EventArgs.Empty); } } } ``` can be rewritten like this: ```cs public class MyBetterClass { private readonly MonitorableSource<int> _property = new MonitorableSource<int>(); public Monitorable<int> Property => _property.Monitorable; } ``` The end result is not absolutely equal because you need to use `.Value` to get property value, but it allows to save a lot of typing and repeating code. This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,38 @@ public class MonitorableSourceExample { private readonly MonitorableSource<int> _a; private readonly MonitorableSource<int> _b; private readonly MonitorableSource<int> _c; public MonitorableSourceExample() { _a = new MonitorableSource<int>(); _b = new MonitorableSource<int>(); _c = new MonitorableSource<int>(); } public Monitorable<int> A => _a.Monitorable; public Monitorable<int> B => _b.Monitorable; public Monitorable<int> C => _c.Monitorable; } public class MonitorableUsingExample { private readonly MonitorableSourceExample _something; public MonitorableUsingExample() { _something = new MonitorableSourceExample(); _something.A.Updated += (sender, args) => { // do something on A changing }; } public int ADouble => _something.A.Value * 2; } -
IanSavchenko renamed this gist
Jan 1, 2018 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
IanSavchenko created this gist
Jan 1, 2018 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,111 @@ 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; } } }