Is this a thread safe collection? Any issues with the code?public class ThreadSafeKeyedObservableCollection<TKey, TItem> : IObservableList, IList<TItem>    where TItem : IKeyProvider<TKey>{    #region Fields    private Dictionary<TKey, TItem> _dict;    private IList<TItem> _collection;    private readonly object _locker = new object();    public event NotifyCollectionChangedEventHandler CollectionChanged;    #endregion    #region Constructors    public ThreadSafeKeyedObservableCollection(List<TItem> members)    {        InitializeFromList(members);    }    // added to avoid calling  nethod in constructor    private void InitializeFromList(List<TItem> members)    {        _dict = new Dictionary<TKey, TItem>();        // leaving as for for speed        foreach (var item in members)        {            _dict.Add(GetKeyFromItem(item), item);  //Throws ArgumentException if duplicates        }        _collection = members;    }    public ThreadSafeKeyedObservableCollection()    {        _dict = new Dictionary<TKey, TItem>();        _collection = new List<TItem>();    }    public ThreadSafeKeyedObservableCollection(int size)    {        _dict = new Dictionary<TKey, TItem>(size);        _collection = new List<TItem>(size);    }    #endregion    private bool _eventsEnabled = true;    public bool EventsEnabled    {        get { return _eventsEnabled; }        set { _eventsEnabled = value; }    }    public void Add(TItem item)    {        lock (_locker)        {            _dict.Add(GetKeyFromItem(item), item);            _collection.Add(item);        }        OnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));    }    public bool AddIfNotPresent(TItem item)    {        bool added = false;        lock (_locker)        {            var key = item.GetKey();            if (!_dict.ContainsKey(key))            {                _dict.Add(GetKeyFromItem(item), item);                _collection.Add(item);                added = true;            }        }        OnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));        return added;    }    public bool AddOrUpdate(TItem item)    {        var added = false;        var updated = false;        var oldItem = default(TItem);        lock (_locker)        {            var key = item.GetKey();            if (!_dict.ContainsKey(key))            {                _dict.Add(key, item);                _collection.Add(item);                added = true;            }            else            {                oldItem = _dict[key];                var index = _collection.IndexOf(oldItem);                if (index >= 0)                {                    _collection[index] = item;                    _dict[key] = item;                    updated = true;                }            }        }        if (updated)            OnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, oldItem));        else if (added)            OnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));        return updated || added;    }    public void AddOrUpdateRange(IList<TItem> items)    {        lock (_locker)        {            foreach (TItem item in items)            {                var key = item.GetKey();                if (!_dict.ContainsKey(key))                {                    _dict.Add(key, item);                    _collection.Add(item);                }                else                {                    var oldItem = _dict[key];                    var index = _collection.IndexOf(oldItem);                    if (index >= 0)                    {                        _collection[index] = item;                        _dict[key] = item;                    }                }            }        }        OnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,            (IList)items));    }    public void AddRange(IList<TItem> items)    {        lock (_locker)        {            foreach (TItem item in items)            {                _dict.Add(GetKeyFromItem(item), item);                _collection.Add(item);            }        }        OnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,            (IList)items));    }    public void AddRange(IList items)    {        lock (_locker)        {            foreach (Object o in items)            {                VerifyValueType(o);                var item = (TItem)o;                _dict.Add(GetKeyFromItem(item), item);                _collection.Add(item);            }        }        OnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,            items));    }    /// <summary>    /// Performs a clear via a NotifyCollectionChangedAction.Replace, which provides an opportunity to     /// get to the old items, for celanup purposes.  If you are unable to use range operations in your    /// code, then use the Reset operation    /// </summary>    public void Clear()    {        BatchReplace(new List<TItem>());    }    /// <summary>    /// Performs a tradition clear, via NotifyCollectionChangedAction.Reset    /// </summary>    public void Reset()    {        lock (_locker)        {            _collection.Clear();        }        OnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));    }    public bool Contains(TItem item)    {        lock (_locker)        {            return _dict.ContainsKey(GetKeyFromItem(item));        }    }    public bool ContainsKey(TKey key)    {        lock (_locker)        {            return _dict.ContainsKey(key);        }    }    public bool ContainsAnyKey(IEnumerable<TKey> items)    {        lock (_locker)        {            return items.Any(_dict.ContainsKey);        }    }    public bool ContainsAllKeys(IEnumerable<TKey> items)    {        lock (_locker)        {            return items.All(_dict.ContainsKey);        }    }    public void CopyTo(TItem[] array, int arrayIndex)    {        lock (_locker)        {            _collection.CopyTo(array, arrayIndex);        }    }    public int Count    {        get        {            lock (_locker)            {                return _collection.Count;            }        }    }    public bool IsReadOnly    {        get        {            return false;        }    }    public bool RemoveByKey(TKey key)    {        if (_dict.ContainsKey(key))        {            return Remove(_dict[key]);        }        return false;    }    public bool Remove(TItem item)    {        bool result;        int index = -1;        lock (_locker)        {            result = _dict.Remove(GetKeyFromItem(item));            if (result)            {                index = _collection.IndexOf(item);                if (index >= 0)                    _collection.RemoveAt(index);            }        }        if (result)            OnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));        return result;    }    public bool RemoveRangeByKey(IList<TKey> keys)    {        IList<TItem> items = new List<TItem>();        foreach (var key in keys)        {            if (_dict.ContainsKey(key))            {                items.Add(_dict[key]);            }            else            {                return false;            }        }        return RemoveRange(items);    }    public bool RemoveRange(IList<TItem> items)    {        bool result = false;        lock (_locker)        {            foreach (TItem item in items)            {                result = _dict.Remove(GetKeyFromItem(item));                if (result)                {                    int index = _collection.IndexOf(item);                    if (index >= 0)                        _collection.RemoveAt(index);                }            }        }        if (result)            OnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, (IList)items));        return result;    }    public bool RemoveRange(IList items)    {        bool result = false;        lock (_locker)        {            foreach (Object o in items)            {                VerifyValueType(o);                var item = (TItem)o;                result = _dict.Remove(GetKeyFromItem(item));                if (result)                {                    _collection.Remove(item);                }            }        }        if (result)            OnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, items));        return result;    }    //Enumerator should be should be returned under lock    public IEnumerator<TItem> GetEnumerator()    {        lock (_locker)        {            var copy = new List<TItem>(_collection);            return copy.GetEnumerator();        }    }    //Delegate the explicit implementation to the implicit one above    IEnumerator IEnumerable.GetEnumerator()    {        return GetEnumerator();    }    public int IndexOf(TItem item)    {        lock (_locker)        {            return _collection.IndexOf(item);        }    }    public void Insert(int index, TItem item)    {        lock (_locker)        {            if (index < 0 || index >= _collection.Count)            {                throw new ArgumentOutOfRangeException(index, Invalid Index);            }            _dict.Add(GetKeyFromItem(item), item);            _collection.Insert(index, item);        }        OnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));    }    public void RemoveAt(int index)    {        TItem result;        lock (_locker)        {            if (_collection.Count == 0 || _collection.Count <= index)                throw new ArgumentOutOfRangeException(index, InvalidIndex);            result = _collection[index];            _collection.RemoveAt(index);            _dict.Remove(GetKeyFromItem(result));        }        OnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, result, index));    }    public TItem this[TKey key]    {        get        {            lock (_locker)            {                TItem result;                _dict.TryGetValue(key, out result);                return result;            }        }    }    public TItem this[int index]    {        get        {            lock (_locker)            {                return _collection[index];            }        }        set        {            lock (_locker)            {                if (index < 0 || index >= _collection.Count)                {                    throw new ArgumentOutOfRangeException();                }                _collection[index] = value;                _dict.Add(GetKeyFromItem(value), value);            }            OnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value, index));        }    }    public void BatchReplace(List<TItem> newItems)    {        List<TItem> oldItems;        lock (_locker)        {            // If both lists are 0, a range exception is thrown            if (newItems.Count == 0 && _collection.Count == 0) return;            oldItems = _collection.ToList();            _collection.Clear();            _dict.Clear();            foreach (TItem data in newItems)            {                _collection.Add(data);                _dict.Add(GetKeyFromItem(data), data);            }        }        OnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace,            newItems, oldItems));    }    public void BatchRemove(List<TItem> oldItems)    {        var newItems = new List<TItem>();        lock (_locker)        {            foreach (TItem data in oldItems)            {                _collection.Remove(data);                _dict.Remove(GetKeyFromItem(data));            }        }        OnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace,            newItems, oldItems));    }    public void UpdateCollection(List<TItem> changedItems)    {        var newItems = new List<TItem>();        lock (_locker)        {            foreach (var ci in changedItems)            {                var key = ci.GetKey();                var index = _collection.IndexOf(_dict[key]);                _dict[key] = ci;                if (index > -1)                    _collection[index] = ci;                else                    _collection.Add(ci);            }        }        OnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItems, changedItems));    }    public void OnCollectionChanged(Object source, NotifyCollectionChangedEventArgs e)    {        if (EventsEnabled && CollectionChanged != null)            CollectionChanged(source, e);    }    protected TKey GetKeyFromItem(TItem i)    {        TKey key = i.GetKey();        return key;    }    #region IList Members    int IList.Add(object value)    {        VerifyValueType(value);        lock (_locker)        {            Add((TItem)value);            return Count - 1;        }    }    void IList.Clear()    {        Clear();    }    bool IList.Contains(object value)    {        if (IsCompatibleObject(value))        {            return Contains((TItem)value);        }        return false;    }    int IList.IndexOf(object value)    {        if (IsCompatibleObject(value))        {            return IndexOf((TItem)value);        }        return -1;    }    void IList.Insert(int index, object value)    {        VerifyValueType(value);        Insert(index, (TItem)value);    }    bool IList.IsFixedSize    {        get { return false; }    }    bool IList.IsReadOnly    {        get { return false; }    }    void IList.Remove(object value)    {        if (IsCompatibleObject(value))        {            Remove((TItem)value);        }    }    void IList.RemoveAt(int index)    {        RemoveAt(index);    }    object IList.this[int index]    {        get        {            return this[index];        }        set        {            VerifyValueType(value);            this[index] = (TItem)value;        }    }    #endregion    #region ICollection Members    void ICollection.CopyTo(Array array, int index)    {        var arg = array as TItem[];        if (arg != null)            CopyTo(arg, index);    }    int ICollection.Count    {        get { return Count; }    }    bool ICollection.IsSynchronized    {        get { return true; }    }    object ICollection.SyncRoot    {        get { return _locker; }    }    private static bool IsCompatibleObject(object value)    {        if ((value is TItem) || (value == null && !typeof(TItem).IsValueType))        {            return true;        }        return false;    }    private static void VerifyValueType(object value)    {        if (!IsCompatibleObject(value))        {            throw new ArgumentException(Invalid type);        }    }    #endregion    public IEnumerable<TKey> Keys()    {        return _dict.Keys.ToList();    }    public TItem TryFindByKey(TKey key, out bool result)    {        if (_dict.ContainsKey(key))        {            result = true;            return _dict[key];        }        result = false;        return default(TItem);    }}public interface IObservableList : INotifyCollectionChanged, IList{}public interface IKeyProvider<out TK>{    TK GetKey();}
ThreadSafeKeyedObservableCollection
c#;multithreading