Created
November 27, 2012 22:01
-
-
Save MrZoidberg/4157432 to your computer and use it in GitHub Desktop.
Dictionary that smartly track changes of the values
This file contains 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 characters
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
var dict = new SmartTrackingDictionary<string, object>(); | |
dict["int"] = 1; | |
dict["int"] = 2; | |
Console.WriteLine("key 'int' {0}", dict.IsChanged("int") ? "has changed" : "hasn't changed"); | |
dict["str"] = "string1"; | |
dict["str"] = "string2"; | |
Console.WriteLine("key 'str' {0}", dict.IsChanged("str") ? "has changed" : "hasn't changed"); | |
} | |
} | |
public class SmartTrackingDictionary<TKey, TValue> : IDictionary<TKey, TValue> | |
{ | |
private const string CountString = "Count"; | |
private const string IndexerName = "Item[]"; | |
private const string KeysName = "Keys"; | |
private const string ValuesName = "Values"; | |
private IDictionary<TKey, TValue> _Dictionary; | |
protected IDictionary<TKey, TValue> Dictionary | |
{ | |
get { return _Dictionary; } | |
} | |
private Dictionary<TKey, bool> _changesDict; | |
#region Constructors | |
public SmartTrackingDictionary() | |
{ | |
_Dictionary = new Dictionary<TKey, TValue>(); | |
_changesDict = new Dictionary<TKey, bool>(); | |
} | |
public SmartTrackingDictionary(IDictionary<TKey, TValue> dictionary) | |
{ | |
_Dictionary = new Dictionary<TKey, TValue>(dictionary); | |
_changesDict = new Dictionary<TKey, bool>(); | |
} | |
public SmartTrackingDictionary(IEqualityComparer<TKey> comparer) | |
{ | |
_Dictionary = new Dictionary<TKey, TValue>(comparer); | |
_changesDict = new Dictionary<TKey, bool>(); | |
} | |
public SmartTrackingDictionary(int capacity) | |
{ | |
_Dictionary = new Dictionary<TKey, TValue>(capacity); | |
_changesDict = new Dictionary<TKey, bool>(); | |
} | |
public SmartTrackingDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) | |
{ | |
_Dictionary = new Dictionary<TKey, TValue>(dictionary, comparer); | |
_changesDict = new Dictionary<TKey, bool>(); | |
} | |
public SmartTrackingDictionary(int capacity, IEqualityComparer<TKey> comparer) | |
{ | |
_Dictionary = new Dictionary<TKey, TValue>(capacity, comparer); | |
_changesDict = new Dictionary<TKey, bool>(); | |
} | |
#endregion | |
#region IDictionary<TKey,TValue> Members | |
public void Add(TKey key, TValue value) | |
{ | |
Insert(key, value, true); | |
} | |
public bool ContainsKey(TKey key) | |
{ | |
return Dictionary.ContainsKey(key); | |
} | |
public ICollection<TKey> Keys | |
{ | |
get { return Dictionary.Keys; } | |
} | |
public bool Remove(TKey key) | |
{ | |
if (key == null) throw new ArgumentNullException("key"); | |
TValue value; | |
Dictionary.TryGetValue(key, out value); | |
var removed = Dictionary.Remove(key); | |
if (removed) | |
{ | |
_changesDict.Remove(key); | |
} | |
return removed; | |
} | |
public bool TryGetValue(TKey key, out TValue value) | |
{ | |
return Dictionary.TryGetValue(key, out value); | |
} | |
public ICollection<TValue> Values | |
{ | |
get { return Dictionary.Values; } | |
} | |
public TValue this[TKey key] | |
{ | |
get | |
{ | |
return Dictionary[key]; | |
} | |
set | |
{ | |
Insert(key, value, false); | |
} | |
} | |
#endregion | |
#region ICollection<KeyValuePair<TKey,TValue>> Members | |
public void Add(KeyValuePair<TKey, TValue> item) | |
{ | |
Insert(item.Key, item.Value, true); | |
} | |
public void Clear() | |
{ | |
if (Dictionary.Count > 0) | |
{ | |
Dictionary.Clear(); | |
_changesDict.Clear(); | |
} | |
} | |
public bool Contains(KeyValuePair<TKey, TValue> item) | |
{ | |
return Dictionary.Contains(item); | |
} | |
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) | |
{ | |
Dictionary.CopyTo(array, arrayIndex); | |
} | |
public int Count | |
{ | |
get { return Dictionary.Count; } | |
} | |
public bool IsReadOnly | |
{ | |
get { return Dictionary.IsReadOnly; } | |
} | |
public bool Remove(KeyValuePair<TKey, TValue> item) | |
{ | |
return Remove(item.Key); | |
} | |
#endregion | |
#region IEnumerable<KeyValuePair<TKey,TValue>> Members | |
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() | |
{ | |
return Dictionary.GetEnumerator(); | |
} | |
#endregion | |
#region IEnumerable Members | |
IEnumerator IEnumerable.GetEnumerator() | |
{ | |
return ((IEnumerable)Dictionary).GetEnumerator(); | |
} | |
#endregion | |
public void AddRange(IDictionary<TKey, TValue> items) | |
{ | |
if (items == null) throw new ArgumentNullException("items"); | |
if (items.Count > 0) | |
{ | |
if (items.Keys.Any((k) => Dictionary.ContainsKey(k))) | |
throw new ArgumentException("An item with the same key has already been added."); | |
else | |
foreach (var item in items) | |
{ | |
Dictionary.Add(item); | |
_changesDict.Add(item.Key, false); | |
} | |
} | |
} | |
private void Insert(TKey key, TValue value, bool add) | |
{ | |
if (key == null) throw new ArgumentNullException("key"); | |
TValue item; | |
if (Dictionary.TryGetValue(key, out item)) | |
{ | |
if (add) throw new ArgumentException("An item with the same key has already been added."); | |
if (IsValuesEqual(item, value)) return; | |
Dictionary[key] = value; | |
_changesDict[key] = true; | |
} | |
else | |
{ | |
Dictionary[key] = value; | |
_changesDict[key] = false; | |
} | |
} | |
private bool IsValuesEqual(TValue value1, TValue value2) | |
{ | |
if (value1 == null && value2 == null) | |
return true; | |
if (ReferenceEquals(null, value2)) return false; | |
if (ReferenceEquals(null, value2)) return false; | |
Type valueType = value1.GetType(); | |
if (valueType != value2.GetType()) | |
return false; | |
if (valueType.IsValueType) | |
{ | |
return value1.Equals(value2); | |
} | |
//will not work for complex objects | |
//if (ReferenceEquals(value1, value2)) return true; | |
var specificEquals = valueType.GetMethod("Equals", new Type[] { valueType }); | |
if (specificEquals != null && | |
specificEquals.ReturnType == typeof(bool)) | |
{ | |
return (bool)specificEquals.Invoke(value1, new object[] { value2 }); | |
} | |
return value1.Equals(value2); | |
} | |
public bool IsChanged(TKey key) | |
{ | |
return _changesDict[key]; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment