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.

Revisions

  1. IanSavchenko revised this gist Jan 1, 2018. 3 changed files with 30 additions and 28 deletions.
    4 changes: 3 additions & 1 deletion README.md
    Original 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>();

    public Monitorable<int> Property => _property.Monitorable;
    // Exposes getter-only property Property.Value and event Property.Updated
    public Monitorable<int> Property => _property.Monitorable;
    }
    ```

    52 changes: 25 additions & 27 deletions example.cs
    Original 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 class MonitorableSourceExample
    public MonitorableSourceExample()
    {
    private readonly MonitorableSource<int> _a;
    private readonly MonitorableSource<int> _b;
    private readonly MonitorableSource<int> _c;
    _a = new MonitorableSource<int>();
    _b = new MonitorableSource<int>();
    _c = new MonitorableSource<int>();
    }

    public MonitorableSourceExample()
    {
    _a = new MonitorableSource<int>();
    _b = new MonitorableSource<int>();
    _c = new MonitorableSource<int>();
    }
    public Monitorable<int> A => _a.Monitorable;

    public Monitorable<int> A => _a.Monitorable;
    public Monitorable<int> B => _b.Monitorable;

    public Monitorable<int> B => _b.Monitorable;
    public Monitorable<int> C => _c.Monitorable;
    }

    public Monitorable<int> C => _c.Monitorable;
    }

    public class MonitorableUsingExample
    {
    private readonly MonitorableSourceExample _something;

    public class MonitorableUsingExample
    public MonitorableUsingExample()
    {
    private readonly MonitorableSourceExample _something;

    public MonitorableUsingExample()
    _something = new MonitorableSourceExample();
    _something.A.Updated += (sender, args) =>
    {
    _something = new MonitorableSourceExample();
    _something.A.Updated += (sender, args) =>
    {
    // do something on A changing
    };
    }


    public int ADouble => _something.A.Value * 2;
    // do something on A changing
    };
    }

    public int ADouble => _something.A.Value * 2;
    }
    2 changes: 2 additions & 0 deletions monitorable.cs
    Original 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>
  2. IanSavchenko revised this gist Jan 1, 2018. 2 changed files with 79 additions and 0 deletions.
    41 changes: 41 additions & 0 deletions README.md
    Original 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.
    38 changes: 38 additions & 0 deletions example.cs
    Original 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;
    }
  3. IanSavchenko renamed this gist Jan 1, 2018. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  4. IanSavchenko created this gist Jan 1, 2018.
    111 changes: 111 additions & 0 deletions gistfile1.txt
    Original 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;
    }
    }
    }