This is my attempt to write a data-bindable, platform independent property that doesn't require any literal strings. The basic idea is to basically have the same functionality as IObservable, but with a value which has a getter (and a setter in a derived interface). The core interface only has a getter:public interface IPropertySource<out T>{    T Value { get; }    IDisposable RawSubscribe(Action rawObserver);}Since it has a getter, the Action in RawSubscribe doesn't need to receive a value. This can be turned into a monad and add Linq operators with some extra code (I've removed all the null checks to make it shorter) (note that this code requires a reference to reactive extensions):internal class ExplicitPropertySource<T> : IPropertySource<T>{    private readonly Func<T> _GetValue;    private readonly Func<Action, IDisposable> _RawSubscribe;    public T Value    {        get { return _GetValue(); }    }    public ExplicitPropertySource(Func<Action, IDisposable> rawSubscribe, Func<T> getValue)    {        _RawSubscribe = rawSubscribe;        _GetValue = getValue;    }    public IDisposable RawSubscribe(Action rawObserver)    {        return _RawSubscribe(rawObserver);    }}public static partial class PropertySource{    public static IPropertySource<T> Create<T>(Func<Action, IDisposable> rawSubscribe, Func<T> getValue)    {        return new ExplicitPropertySource<T>(rawSubscribe, getValue);    }    public static IPropertySource<T> Return<T>(T value)    {        return PropertySource.Create(observer => Disposable.Empty, () => value);    }    public static IPropertySource<T> Distinct<T>(this IPropertySource<T> source, IEqualityComparer<T> comparer)    {        return PropertySource.Create(            action =>            {                T cachedValue = default(T);                Action<T> sendAndCache = value =>                {                    action();                    cachedValue = value;                };                Action sendIfChanged = () =>                {                    var value = source.Value;                    if (!comparer.Equals(value, cachedValue))                        sendAndCache(value);                };                Action observer = null;                observer = () =>                {                    observer = sendIfChanged;                    sendAndCache(source.Value);                };                return source.RawSubscribe(() =>                {                    observer();                });            },            () => source.Value        );    }    public static IPropertySource<T> Distinct<T>(this IPropertySource<T> source)    {        return source.Distinct(EqualityComparer<T>.Default);    }    public static IPropertySource<T> Eager<T>(this IPropertySource<T> source)    {        return PropertySource.Create(            action =>            {                action();                return source.RawSubscribe(action);            },            () => source.Value        );    }    public static IPropertySource<T> Lazy<T>(this IPropertySource<T> source)    {        return PropertySource.Create(            action =>            {                Action send = () => { };                var subscription = source.RawSubscribe(() => send());                send = action;                return subscription;            },            () => source.Value        );    }    public static IPropertySource<TResult> SelectMany<TSource, TResult>(this IPropertySource<TSource> source, Func<TSource, IPropertySource<TResult>> selector)    {        return PropertySource.Create(            observer =>            {                IDisposable rightSubscription = Disposable.Empty;                Func<TSource, IPropertySource<TResult>> reattachRight = leftValue =>                {                    rightSubscription.Dispose();                    var rightSource = selector(leftValue);                    rightSubscription = rightSource.Lazy().RawSubscribe(observer);                    return rightSource;                };                IDisposable leftSubscription = source.Lazy().RawSubscribe(() =>                {                    reattachRight(source.Value);                    observer();                });                reattachRight(source.Value);                return Disposable.Create(() =>                {                    leftSubscription.Dispose();                    rightSubscription.Dispose();                });            },            () => selector(source.Value).Value        );    }    public static IPropertySource<TResult> Select<TSource, TResult>(this IPropertySource<TSource> source, Func<TSource, TResult> selector)    {        return source.SelectMany(value => PropertySource.Return(selector(value)));    }    public static IPropertySource<TResult> Merge<TLeft, TRight, TResult>(this IPropertySource<TLeft> left, IPropertySource<TRight> right, Func<TLeft, TRight, TResult> selector)    {        return left.SelectMany(leftValue =>            right.Select(rightValue => selector(leftValue, rightValue))        );    }}Since I'm using WinForms, I can then create these using standard WinForms properties:public class MemberAccessInfo{    public readonly object Instance;    public readonly string MemberName;    public MemberAccessInfo(object instance, string memberName)    {        Instance = instance;        MemberName = memberName;    }}public static partial class PropertySource{    private static MemberAccessInfo GetMemberAccessInfo<T>(Expression<Func<T>> memberAccessExpression)    {        LambdaExpression lambda = (LambdaExpression)memberAccessExpression;        if (lambda.Body.NodeType != ExpressionType.MemberAccess)            throw new Exception(Expression must be a member access.);        MemberExpression memberExpr = (MemberExpression)lambda.Body;        var instance = Expression.Lambda(memberExpr.Expression).Compile().DynamicInvoke();        return new MemberAccessInfo(instance, memberExpr.Member.Name);    }    internal static IPropertySource<T> FromProperty<T>(object instance, Type instanceType, PropertyInfo memberInfo)    {        var eventInfo = instanceType.GetEvent(memberInfo.Name + Changed, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);        Func<Action, IDisposable> subscribe;        if (eventInfo != null && eventInfo.EventHandlerType == typeof(EventHandler))        {            subscribe = observer =>            {                EventHandler handler = (s, e) => observer();                eventInfo.AddEventHandler(instance, handler);                return Disposable.Create(() => eventInfo.RemoveEventHandler(instance, handler));            };        }        else        {            var notifyPropertyChanged = instance as INotifyPropertyChanged;            if (notifyPropertyChanged == null)                throw new Exception(This member cannot be observed.);            subscribe = observer =>            {                PropertyChangedEventHandler handler = (s, e) =>                {                    if (e.PropertyName == memberInfo.Name)                        observer();                };                notifyPropertyChanged.PropertyChanged += handler;                return Disposable.Create(() => notifyPropertyChanged.PropertyChanged -= handler);            };        }        return Create(subscribe, () => (T)memberInfo.GetValue(instance));    }    public static IPropertySource<T> FromProperty<T>(object instance, string propertyName)    {        var type = instance.GetType();        return FromProperty<T>(instance, type, type.GetProperty(propertyName));    }    public static IPropertySource<T> FromProperty<T>(Expression<Func<T>> memberAccessExpression)    {        var propertyInfo = GetMemberAccessInfo(memberAccessExpression);        return FromProperty<T>(propertyInfo.Instance, propertyInfo.MemberName);    }}And finally, subscribe with these:public static partial class PropertySource{    public static IDisposable Subscribe<T>(this IPropertySource<T> source, Action<T> observer, IEqualityComparer<T> comparer)    {        return source.Eager().Distinct(comparer).RawSubscribe(() => observer(source.Value));    }    public static IDisposable Subscribe<T>(this IPropertySource<T> source, Action<T> observer)    {        return source.Eager().Distinct().RawSubscribe(() => observer(source.Value));    }}So I can do stuff like:var firstNameProperty = PropertySource.FromProperty(() => firstNameTextBox.Text);var lastNameProperty = PropertySource.FromProperty(() => lastNameTextBox.Text);var fullNameProperty = firstNameProperty.Merge(lastNameProperty, (fn, ln) => fn +   + ln);var disposable = fullNameProperty.Subscribe(val => fullNameLabel.Text = val);This seems to me like such an obvious missing feature I was wondering if something like this already exists, and if not, whether I should make any improvements to my code. Also, I'm still not 100% my monad is actually a monad, but so far it's been working fine.This whole thing probably doesn't make much sense without the IProperty interface which also has a property setter, but is not a monad (it does have a SelectMany and Select methods though), and can also be created given a WinForms property. I'm not including it here since it's already pretty big, but I uploaded the whole thing to GitHib, and included a small sample WinForms application.
Reactive Properties for easy property binding
c#;winforms;reactive programming
T cachedValue = default(T);Action<T> sendAndCache = value =>{    action();    cachedValue = value;};Action sendIfChanged = () =>{    var value = source.Value;    if (!comparer.Equals(value, cachedValue))        sendAndCache(value);};Action observer = null;observer = () =>{    observer = sendIfChanged;    sendAndCache(source.Value);};return source.RawSubscribe(() =>{    observer();});Here, you create a special initial observer which always sends the value (since there is nothing to compare it to) and then replace it with a normal comparing observer. I think it would be simpler and clearer to have a single observer, along with a variable tracking whether the cachedValue has been set:T cachedValue = default(T);bool cachedValueSet = false;Action observer = () =>{    var value = source.Value;    if (!cachedValueSet || !comparer.Equals(value, cachedValue))    {        action();        cachedValue = value;        cachedValueSet = true;    }};return source.RawSubscribe(observer);() => selector(source.Value).ValueWhat happens if source.Value is null?I'm also wondering about the monad thing: does using LINQ queries on your objects make sense? If not, I probably wouldn't use names like Select or SelectMany.