1

In Visual Studio Color Theme Editor extension if you change some colors, some other related colors will change accordingly. They can become equal to the changed color or be more lighter/darker for example.

I wondered how can this behavior be implemented as generalized as possible. So, I came up with this crazy solution.

using System;
using System.Collections.Generic;
using System.Drawing;

namespace tester {

interface IKeyGet<TKey, TValue> {
    TValue Get(TKey key);
}

interface IKeySet<TKey, TValue> {
    void Set(TKey key, TValue value);
}

interface IKeyGetSet<TKey, TValue> : IKeyGet<TKey, TValue>, IKeySet<TKey, TValue> {
}

class SomeManager<TKey, TValue> : IKeyGetSet<TKey, TValue> {
    protected ICollection<IKeyGet<TKey, TValue>> Getters { get; set; }
    protected ICollection<IKeySet<TKey, TValue>> Setters { get; set; }

    public SomeManager(ICollection<IKeyGet<TKey, TValue>> getters, ICollection<IKeySet<TKey, TValue>> setters) {
        Getters = getters;
        Setters = setters;
    }

    public virtual TValue Get(TKey key) {
        var value = default(TValue);
        foreach (var g in Getters) {
            value = g.Get(key);
        }
        return value;
    }

    public virtual void Set(TKey key, TValue value) {
        foreach (var s in Setters) {
            s.Set(key, value);
        }
    }
}

// Gets value by key, throws if key is not found
class BasicGetter<TKey, TValue> : IKeyGet<TKey, TValue> {
    public IDictionary<TKey, TValue> Source { get; set; }

    public BasicGetter(IDictionary<TKey, TValue> source) {
        Source = source;
    }

    public TValue Get(TKey key) {
        return Source[key];
    }
}

// Can add new key-value pair or update existing
class CreateUpdateSetter<TKey, TValue> : IKeySet<TKey, TValue> {
    public IDictionary<TKey, TValue> Source { get; set; }

    public CreateUpdateSetter(IDictionary<TKey, TValue> source) {
        Source = source;
    }

    public void Set(TKey key, TValue value) {
        Source[key] = value;
    }
}

// Can only update existing key-value pairs, adding new ones is forbidden
class UpdateSetter<TKey, TValue> : IKeySet<TKey, TValue> {
    public IDictionary<TKey, TValue> Source { get; set; }

    public UpdateSetter(IDictionary<TKey, TValue> source) {
        Source = source;
    }

    public void Set(TKey key, TValue value) {
        if (!Source.ContainsKey(key)) {
            throw new KeyNotFoundException();
        } else {
            Source[key] = value;
        }
    }
}

// If key is in RelatedSetters, then invoke all corresponding delegates and set the keys being returned
class RelatedSetter<TKey, TValue> : IKeySet<TKey, TValue> {
    public IDictionary<TKey, IEnumerable<Func<TKey, TValue, KeyValuePair<TKey, TValue>>>> RelatedSetters { get; set; }

    public IKeySet<TKey, TValue> Source { get; set; }

    public RelatedSetter(IKeySet<TKey, TValue> source) {
        Source = source;
        RelatedSetters = new Dictionary<TKey, IEnumerable<Func<TKey, TValue, KeyValuePair<TKey, TValue>>>>();
    }

    public void Set(TKey key, TValue value) {
        IEnumerable<Func<TKey, TValue, KeyValuePair<TKey, TValue>>> related;
        if (RelatedSetters.TryGetValue(key, out related)) {
            foreach (var f in related) {
                var c = f(key, value);
                Source.Set(c.Key, c.Value);
            }
        }
    }
}

// Has read-update behavior
class ColorManager<T> : SomeManager<T, Color> {
    private IDictionary<T, Color> _colorMap;
    private RelatedSetter<T, Color> _relatedSetter;

    public IEnumerable<T> Keys { get { return _colorMap.Keys; } }

    public IDictionary<T, IEnumerable<Func<T, Color, KeyValuePair<T, Color>>>> RelatedColors {
        get {
            return _relatedSetter.RelatedSetters;
        }
        set {
            _relatedSetter.RelatedSetters = value;
        }
    }

    public ColorManager(IDictionary<T, Color> initialColors) :
        base(new List<IKeyGet<T, Color>>(), new List<IKeySet<T, Color>>()) {

        _colorMap = initialColors;
        _relatedSetter = new RelatedSetter<T, Color>(this);

        Getters.Add(new BasicGetter<T, Color>(_colorMap));
        Setters.Add(new UpdateSetter<T, Color>(_colorMap));
        Setters.Add(_relatedSetter);
    }
}

class Program {
    enum AvailableColors { Water, Submarine, Seabed };

    static void Main(string[] args) {
        var cm = new ColorManager<AvailableColors>(new Dictionary<AvailableColors, Color> {
            { AvailableColors.Water, Color.Green },
            { AvailableColors.Submarine, Color.Red },
            { AvailableColors.Seabed, Color.Yellow }
        });

        cm.RelatedColors = new Dictionary<AvailableColors, IEnumerable<Func<AvailableColors, Color, KeyValuePair<AvailableColors, Color>>>> {
            {
                AvailableColors.Water, new List<Func<AvailableColors, Color, KeyValuePair<AvailableColors, Color>>> {
                    (k, v) => { return new KeyValuePair<AvailableColors, Color>(AvailableColors.Submarine, Color.Azure); },
                    (k, v) => { return new KeyValuePair<AvailableColors, Color>(AvailableColors.Seabed, Color.Red); }
                }
            },
            {
                AvailableColors.Seabed, new List<Func<AvailableColors, Color, KeyValuePair<AvailableColors, Color>>> {
                    (k, v) => { return new KeyValuePair<AvailableColors, Color>(AvailableColors.Submarine, Color.Black); },
                }
            }
        };

        cm.Set(AvailableColors.Water, Color.AliceBlue);

        // Expected colors: 
        //
        // Water - AliceBlue
        // Submarine - Black
        // Seabed - Red

        foreach (var key in cm.Keys) {
            Console.WriteLine($"{key}: {cm.Get(key)}");
        }
    }
}
}

The core idea is to take Dictionary-like class, that can get and set values by keys, and enhance its getters and setters with custom behavior. I think this can be accomplished better with mixins, like in Python, but as C# does not support mixins, collections of functions replace them.

For me, benefit of this solution is behavior customization while retaining simple Get/Set interface.

Although, I'm a little scared of intimidating lines of code like:

public IDictionary<TKey, IEnumerable<Func<TKey, TValue, KeyValuePair<TKey, TValue>>>> RelatedSetters { get; set; }

and

cm.RelatedColors = new Dictionary<AvailableColors, IEnumerable<Func<AvailableColors, Color, KeyValuePair<AvailableColors, Color>>>> {...};

whereas in Python it would be just

cm.RelatedColors = {...}

EDIT:

My question is: "How would you implement collection, in which elements may be related to each other? Such as editing one element may trigger changes in one or more of other elements."

No need for concrete examples, theories will be enough.

4
  • 1
    What's the question? If you just want a review, this should probably be on the code review SE site instead. Sep 2 '16 at 20:26
  • Yeah, maybe it's a wrong site. Can I transfer my question to another site? Or just delete here and create there? Anyway, the actual question may be: "How would you implement collection, in which elements may be related to each other? Such as editing one element may trigger changes in one or more of other elements" Sep 2 '16 at 20:53
  • Sorry, I'm never sure what somebody's supposed to do if they decide their own question was posted on the wrong site. Hopefully somebody else will come along and tell us. Sep 2 '16 at 21:04
  • @saintcrawler: if you are still interested in getting your question migrated, flag your question as "in need for moderator intervention" and leave a note where you want it to be moved.
    – Doc Brown
    Sep 3 '16 at 9:00
0

I would simply use INotifyPropertyChanged.

Basically, a given color doesn't care what other colors depend on it. Inversely, a given color which is based on another one should be interested in the changes which occur to the base color.

You can see the exact same model in Windows Forms/WPF interfaces. If a control is bound to a variable, it's up to the control to track when the variable changes its value—this is the most popular use of INotifyPropertyChanged.

6
  • While reading about INotifyPropertyChanged at your link, I came across BindingList class. And I think this can be the solution I need. Thanks! Sep 3 '16 at 9:14
  • @saintcrawler: How would BindingList<T> help in your case? I believe that you're still doing it the opposite way, unless, of course, a given color may be based not on zero or one colors, but zero, one or many. Sep 3 '16 at 9:24
  • Yes, zero, one or many. It's not about colors (it's just an example), but about relations between some abstract elements. Sep 3 '16 at 9:47
  • @saintcrawler: I'm curious, what would be the purpose of such thing? Not color theme editor, obviously, since you'll have a 0..1 relation instead of 0..n, so what else? Sep 3 '16 at 10:23
  • I don't know, yet :) I mentioned that this is an abstract thing. It was one of my random thoughts about high-level programming :) But, maybe this can be applicable to modelling of electronic circuits, for example? Sep 3 '16 at 10:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.