None None - 3 months ago 27
C# Question

Automatic dictionary key?

I kept googling for some time, and I found that the best way that enables you to have a list containing variables with a corresponding unique key is a

HashTable
or a
Dictionary
, but I didn't find anything that enables you to have automatic keys(of type integer). I want to call a function that adds an object(passed as a parameter) to the dictionary and returns the automatically generated key(int), and without any key duplicates. How could I accomplish this? I am completely struggling!

EDIT: To clarify things up. This is a server, and I want to assign a unique key for each client. If I use the maximum key value, this value will soon get to the int maximum value on large servers. Because if a client connects then disconnects he leaves behind an unused value which should be reused in order to avoid reaching a very high key maximum value.

Answer

The following should do and it reuses freed up keys:

public class AutoIndexedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable
{
    private readonly Dictionary<TKey, TValue> inner;
    private readonly Func<TKey, TKey> incrementor;
    private readonly Stack<TKey> freeKeys;
    private readonly TKey keySeed;
    private TKey currentKey;

    public AutoIndexedDictionary(TKey keySeed, Func<TKey, TKey> incrementor) 
    {
        if (keySeed == null)
            throw new ArgumentNullException(nameof(keySeed));

        if (incrementor == null)
            throw new ArgumentNullException(nameof(incrementor));

        inner = new Dictionary<TKey, TValue>();
        freeKeys = new Stack<TKey>();
        currentKey = keySeed;
    }

    public TKey Add(TValue value) //returns the used key
    {
        TKey usedKey;
        if (freeKeys.Count > 0)
        {
            usedKey = freeKeys.Pop();
            inner.Add(usedKey, value);
        }
        else
        {
            usedKey = currentKey;
            inner.Add(usedKey, value);
            currentKey = incrementor(currentKey);
        }

        return usedKey;
    }

    public void Clear()
    {
        inner.Clear();
        freeKeys.Clear();
        currentKey = keySeed;
    }

    public bool Remove(TKey key)
    {
        if (inner.Remove(key))
        {
            if (inner.Count > 0)
            {
                freeKeys.Push(key);
            }
            else
            {
                freeKeys.Clear();
                currentKey = keySeed;
            }

            return true;
        }

        return false;
    }

    public bool TryGetValue(TKey key, out TValue value) => inner.TryGetValue(key, out value);
    public TValue this[TKey key] => inner[key];
    public bool ContainsKey(TKey key) => inner.ContainsKey(key);
    public bool ContainsValue(TValue value) => inner.ContainsValue(value);
    public int Count => inner.Count;
    public Dictionary<TKey,TValue>.KeyCollection Keys => inner.Keys;
    public Dictionary<TKey, TValue>.ValueCollection Values => inner.Values;
    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => inner.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)inner).GetEnumerator();
}

Disclaimer: I haven't tested this code, it could have a few pesty bugs of little importance, the general approach is sound.