0

How do I ensure that in a Dictionary Generic, Value Attribute always equals Key * 2? I wrote a Inheritance class from dictionary with method AddItem. Now, I want to hide the original base class? How would this be done? Or is there anyway or option to ensure Data Integrity? This is a simple example, will utilize more complicated examples at work

public class MultipleBy2Test:Dictionary<int, int>
{
    public void AddItem(int OriginalAmount int DoubleAmount)
    {
        base.Add(OriginalAmount, OriginalAmount * 2);
    }
}
Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
  • What methods/properties would you want to expose from the Dictionary? – Jonathon Chase Jul 01 '19 at 22:46
  • 1
    Your `AddItem` declaration won't compile. – NetMage Jul 01 '19 at 22:55
  • 2
    Why take a `DoubleAmount` parameter in `AddItem` if you don't even use it? And why even have a dictionary which just stores a value and then double the value? You don't need a dictionary for that. Just double the value when you need it doubled. – mjwills Jul 01 '19 at 22:58
  • 1
    @mjwills Assuming membership in the `Dictionary` matters, replace it with a `HashSet`? – NetMage Jul 01 '19 at 23:01
  • I would like to expose all methods of Dictionary, except add , –  Jul 01 '19 at 23:02
  • 2
    But why do you need the methods of `Dictionary` when you aren't using it as a `Dictionary`? – NetMage Jul 01 '19 at 23:06
  • Please, as per @NetMage's first comment, change your question code **so it compiles**. – mjwills Jul 01 '19 at 23:12
  • 2
    Another option is to **shadow** the method (https://stackoverflow.com/questions/392721/difference-between-shadowing-and-overriding-in-c). And then add the validation you want there. This will work **as long as they don't cast the object to `Dictionary` before calling `Add`.** – mjwills Jul 01 '19 at 23:36

1 Answers1

3

The Add method isn't virtual, so you can't override it. The only choice that leaves is to encapsulate the dictionary.

public class MultipleBy2Test
{
    private readonly Dictionary<int, int> _values = new Dictionary<int, int>();

    public void AddItem(int originalAmount)
    {
        _values.Add(originalAmount, originalAmount * 2);
    }
}

Now the class doesn't inherit from Dictionary<int, int> and nothing in its public interface allows access to the dictionary. Data integrity is ensured because nothing but your method can add anything to the dictionary.

Ideally you would just add a few methods to retrieve values and be done, if that were an option.

If you want all of the other methods of a dictionary then you would implement IDictionary<int, int>. Because of what a nuisance this is, I'd start with a generic implementation and make the Add methods virtual. That way if you want another dictionary with different logic you don't have to create another class and implement all this stuff again.

public class MyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
    private readonly IDictionary<TKey, TValue> _innerDictionary 
        = new Dictionary<TKey, TValue>();

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        return _innerDictionary.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public virtual void Add(KeyValuePair<TKey, TValue> item)
    {
        _innerDictionary.Add(item);
    }

    public void Clear()
    {
        _innerDictionary.Clear();
    }

    public bool Contains(KeyValuePair<TKey, TValue> item)
    {
        return _innerDictionary.Contains(item);
    }

    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
    {
        _innerDictionary.CopyTo(array, arrayIndex);
    }

    public bool Remove(KeyValuePair<TKey, TValue> item)
    {
        return _innerDictionary.Remove(item);
    }

    public int Count => _innerDictionary.Count;
    public bool IsReadOnly => _innerDictionary.IsReadOnly;
    public bool ContainsKey(TKey key)
    {
        return _innerDictionary.ContainsKey(key);
    }

    public virtual void Add(TKey key, TValue value)
    {
        _innerDictionary.Add(key, value);
    }

    public bool Remove(TKey key)
    {
        return _innerDictionary.Remove(key);
    }

    public bool TryGetValue(TKey key, out TValue value)
    {
        return _innerDictionary.TryGetValue(key, out value);
    }

    public virtual TValue this[TKey key]
    {
        get => _innerDictionary[key];
        set => _innerDictionary[key] = value;
    }

    public ICollection<TKey> Keys => _innerDictionary.Keys;
    public ICollection<TValue> Values => _innerDictionary.Values;
}

That gets you a dictionary implementation where you can override the Add methods. You can reject values that doesn't meet your requirements. You could create other overloads for Add if you want to.

public class InheritedDictionaryWithValidation : MyDictionary<int, int>
{
    public override void Add(KeyValuePair<int, int> item)
    {
        Add(item.Key, item.Value);
    }

    public override void Add(int key, int value)
    {
        ValidateEntry(key, value);
        base.Add(key, value);
    }

    public override int this[int key]
    {
        get => base[key];

        set
        {
            ValidateEntry(key, value);
            base[key] = value;
        }
    }

    private void ValidateEntry(int key, int value)
    {
        if (value != key * 2)
            throw new ArgumentException("You've violated some rule.");
    }
}

You could even go a step further to avoid duplication and introduce an intermediate abstract version for validation:

public abstract class ValidatedDictionary<TKey, TValue> : MyDictionary<TKey, TValue>
{
    public override void Add(KeyValuePair<TKey, TValue> item)
    {
        Add(item.Key, item.Value);
    }

    public override void Add(TKey key, TValue value)
    {
        ValidateEntry(key, value);
        base.Add(key, value);
    }

    public override TValue this[TKey key]
    {
        get => base[key];

        set
        {
            ValidateEntry(key, value);
            base[key] = value;
        }
    }

    private void ValidateEntry(TKey key, TValue value)
    {
        if (!IsEntryValid(key, value))
            throw new ArgumentException("The entry is not valid.");
    }

    protected abstract bool IsEntryValid(TKey key, TValue value);
}

Now you can create dictionaries that validate entries without duplicating anything:

public class MyIntDictionaryWithValidation : ValidatedDictionary<int, int>
{
    protected override bool IsEntryValid(int key, int value)
    {
        return value == key * 2;
    }
}
Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
  • I would like to expose all methods except Add, that's why trying to inherit dictionary –  Jul 01 '19 at 22:51
  • 1
    Note that if you make `Add` overridable you likely need `this[TKey] {set}` to be virtual too as it is the other way to update values. – Alexei Levenkov Jul 02 '19 at 01:29
  • @AlexeiLevenkov - Thanks. That's fixed. That in turn led to a tiny refactoring so that someone inheriting from this wouldn't have to remember to override all three places where entries are added or updated. – Scott Hannen Jul 03 '19 at 12:59