Nell'estensione dell'editor dei temi di colore di Visual Studio se cambi alcuni colori, alcuni altri colori correlati cambieranno di conseguenza. Possono diventare uguali al colore modificato o essere più chiari / scuri per esempio.
Mi sono chiesto come può essere implementato questo comportamento il più generale possibile. Quindi, ho pensato a questa pazza soluzione.
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)}");
}
}
}
}
L'idea di base è prendere una classe simile al dizionario, che possa ottenere e impostare i valori con le chiavi e migliorare i suoi getter e setter con un comportamento personalizzato. Penso che questo si possa ottenere meglio con i mixin, come in Python, ma siccome C # non supporta i mixin, le collezioni di funzioni li sostituiscono.
Per me, il beneficio di questa soluzione è la personalizzazione del comportamento pur conservando la semplice interfaccia Get / Set.
Anche se, ho un po 'paura delle linee intimidatorie di codice come:
public IDictionary<TKey, IEnumerable<Func<TKey, TValue, KeyValuePair<TKey, TValue>>>> RelatedSetters { get; set; }
e
cm.RelatedColors = new Dictionary<AvailableColors, IEnumerable<Func<AvailableColors, Color, KeyValuePair<AvailableColors, Color>>>> {...};
mentre in Python sarebbe solo
cm.RelatedColors = {...}
Modifica
La mia domanda è: "Come implementeresti la raccolta, in cui gli elementi possono essere correlati tra loro? Come la modifica di un elemento può far scattare cambiamenti in uno o più altri elementi."
Non c'è bisogno di esempi concreti, le teorie saranno sufficienti.