Skip to main content

Thread-safe C# Dictionary class.

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;

namespace TinyIoC
{
    public class SafeDictionary<TKey, TValue> : IDisposable
    {
        private readonly ReaderWriterLockSlim _padlock = new ReaderWriterLockSlim();
        private readonly Dictionary<TKey, TValue> _Dictionary = new Dictionary<TKey, TValue>();

        public TValue this[TKey key]
        {
            set
            {
                _padlock.EnterWriteLock();
                try
                {
                    TValue current;
                    if (_Dictionary.TryGetValue(key, out current))
                    {
                        var disposable = current as IDisposable;

                        if (disposable != null)
                            disposable.Dispose();
                    }
                    _Dictionary[key] = value;
                }
                finally
                {
                    _padlock.ExitWriteLock();
                }
            }
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            _padlock.EnterReadLock();
            try
            {
                return _Dictionary.TryGetValue(key, out value);
            }
            finally
            {
                _padlock.ExitReadLock();
            }
        }

        public bool Remove(TKey key)
        {
            _padlock.EnterWriteLock();
            try
            {
                return _Dictionary.Remove(key);
            }
            finally
            {
                _padlock.ExitWriteLock();
            }
        }

        public void Clear()
        {
            _padlock.EnterWriteLock();
            try
            {
                _Dictionary.Clear();
            }
            finally
            {
                _padlock.ExitWriteLock();
            }
        }

        public IEnumerable<TKey> Keys
        {
            get
            {
                _padlock.EnterReadLock();
                try
                {
                    return new List<TKey>(_Dictionary.Keys);
                }
                finally
                {
                    _padlock.ExitReadLock();
                }
            }
        }

        public void Dispose()
        {
            _padlock.EnterWriteLock();
            try
            {
                var disposableItems = from item in _Dictionary.Values
                                      where item is IDisposable
                                      select item as IDisposable;

                foreach (var item in disposableItems)
                {
                    item.Dispose();
                }
            }
            finally
            {
                _padlock.ExitWriteLock();
            }

            GC.SuppressFinalize(this);
        }

    }
}

// ---------------------------------------------------------------------------------------------------------
// Another thread-safe generic dictionary class
// Source: https://github.com/madhatter22/LinqToLdap/blob/master/LinqToLdap/Collections/SafeDictionary.cs
// ---------------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace LinqToLdap.Collections
{
    internal class SafeDictionary<TKey, TValue>
    {
        private ReaderWriterLockSlim _locker = new ReaderWriterLockSlim();
        private Dictionary<TKey, TValue> _internal = new Dictionary<TKey, TValue>();

        ~SafeDictionary()
        {
            var locker = _locker;
            if (locker != null)
            {
                locker.Dispose();
                _locker = locker = null;
            }
            var dictionary = _internal;
            if (dictionary != null)
            {
                dictionary.Clear();
                _internal = dictionary = null;
            }
        }

        public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
        {
            _locker.EnterReadLock();
            try
            {
                if (_internal.ContainsKey(key))
                {
                    return _internal[key];
                }
            }
            finally
            {
                _locker.ExitReadLock();
            }
            var value = valueFactory(key);
            _locker.EnterWriteLock();
            try
            {
                if (!_internal.ContainsKey(key))
                {
                    _internal.Add(key, value);
                }
                return _internal[key];
            }
            finally
            {
                _locker.ExitWriteLock();
            }
        }

        public ReadOnlyDictionary<TKey, TValue> ToReadOnly()
        {
            _locker.EnterReadLock();
            try
            {
                return new ReadOnlyDictionary<TKey, TValue>(_internal.ToDictionary(d => d.Key, d => d.Value));
            }
            finally
            {
                _locker.ExitReadLock();
            }
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            _locker.EnterReadLock();
            try
            {
                if (_internal.ContainsKey(key))
                {
                    value = _internal[key];
                    return true;
                }
                value = default;
                return false;
            }
            finally
            {
                _locker.ExitReadLock();
            }
        }

        public void TryAdd(TKey key, TValue value)
        {
            _locker.EnterWriteLock();
            try
            {
                if (!_internal.ContainsKey(key))
                {
                    _internal.Add(key, value);
                }
            }
            finally
            {
                _locker.ExitWriteLock();
            }
        }

        public void AddOrUpdate(TKey key, TValue value)
        {
            _locker.EnterWriteLock();
            try
            {
                _internal[key] = value;
            }
            finally
            {
                _locker.ExitWriteLock();
            }
        }
    }
}