首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >实现IList<T>、IList和INotifyCollectionChanged

实现IList<T>、IList和INotifyCollectionChanged
EN

Code Review用户
提问于 2013-09-03 00:19:36
回答 1查看 17.8K关注 0票数 9

我把这个问题发到堆栈溢出网站上,这是一个离题的话题。

我在我的WPF应用程序中的许多地方使用ObservableCollection来处理非常大的数组。这造成了一个问题,因为ObservableCollection并没有像List那样对容量进行论证。这是一个重要的优化,可以锁定我的内存使用情况,并确保这些集合不会比所需的大得多。

因此,我实现了以下类:

代码语言:javascript
复制
public class ObservableList<T> : IList<T>, IList, INotifyCollectionChanged
{
    public event NotifyCollectionChangedEventHandler CollectionChanged;

    #region Properties
    private List<T> _List
    {
        get;
        set;
    }

    public T this[int index]
    {
        get
        {
            return _List[index];
        }
        set
        {
            if (index < 0 || index >= Count)
                throw new IndexOutOfRangeException("The specified index is out of range.");
            var oldItem = _List[index];
            _List[index] = value;
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldItem, index));
        }
    }

    public int Count
    {
        get
        {
            return _List.Count;
        }
    }

    public bool IsReadOnly
    {
        get
        {
            return ((IList<T>)_List).IsReadOnly;
        }
    }

    bool IList.IsFixedSize
    {
        get
        {
            return ((IList)_List).IsFixedSize;
        }
    }

    object IList.this[int index]
    {
        get
        {
            return ((IList)_List)[index];
        }
        set
        {
            if (index < 0 || index >= Count)
                throw new IndexOutOfRangeException("The specified index is out of range.");
            var oldItem = ((IList)_List)[index];
            ((IList)_List)[index] = value;
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldItem, index));
        }
    }

    public bool IsSynchronized
    {
        get
        {
            return ((IList)_List).IsSynchronized;
        }
    }

    public object SyncRoot
    {
        get
        {
            return ((IList)_List).SyncRoot;
        }
    }
    #endregion

    #region Methods
    private void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
    {
        if (CollectionChanged != null)
            CollectionChanged(this, args);
    }

    public void AddRange(IEnumerable<T> collection)
    {
        _List.AddRange(collection);
        var iList = collection as IList;
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    public int IndexOf(T item)
    {
        return _List.IndexOf(item);
    }

    public void Insert(int index, T item)
    {
        _List.Insert(index, item);
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
    }

    public void RemoveAt(int index)
    {
        if (index < 0 || index >= Count)
            throw new IndexOutOfRangeException("The specified index is out of range.");
        var oldItem = _List[index];
        _List.RemoveAt(index);
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItem, index));
    }

    public void Add(T item)
    {
        _List.Add(item);
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
    }

    public void Clear()
    {
        _List.Clear();
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    public bool Contains(T item)
    {
        return _List.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        _List.CopyTo(array, arrayIndex);
    }

    public bool Remove(T item)
    {
        var result = _List.Remove(item);
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
        return result;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _List.GetEnumerator();
    }

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

    int IList.Add(object value)
    {
        var result = ((IList)_List).Add(value);
        ;
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));
        return result;
    }

    bool IList.Contains(object value)
    {
        return ((IList)_List).Contains(value);
    }

    int IList.IndexOf(object value)
    {
        return ((IList)_List).IndexOf(value);
    }

    void IList.Insert(int index, object value)
    {
        ((IList)_List).Insert(index, value);
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value, index));
    }

    void IList.Remove(object value)
    {
        ((IList)_List).Remove(value);
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, value));
    }

    public void CopyTo(Array array, int index)
    {
        ((IList)_List).CopyTo(array, index);
    }
    #endregion

    #region Initialization
    public ObservableList()
    {
        _List = new List<T>();
    }

    public ObservableList(int capacity)
    {
        _List = new List<T>(capacity);
    }

    public ObservableList(IEnumerable<T> collection)
    {
        _List = new List<T>(collection);
    }
    #endregion
}

它似乎工作得很好,但是我注意到自从切换到这个实现之后,处理时间增加了。接口实现正确了吗?

EN

回答 1

Code Review用户

回答已采纳

发布于 2013-10-16 23:47:55

  1. IList的实现是不一致的:您已经显式地实现了IsFixedSize,而IsSynchronizedSyncRoot没有实现,但是它们都是IList接口的三个部分。您可能应该完全显式地实现IList
  2. 您偶尔会做一些不必要的转换,比如这里的((IList)_List)[index]。只有在必要时才进行强制转换(例如,当将基础List<T>转换为IList时,因为需要访问某些显式实现的接口方法/属性)。
  3. 事件引发方法中存在一个潜在的争用条件:私有空OnCollectionChanged(NotifyCollectionChangedEventArgs args) { if (CollectionChanged != null) CollectionChanged(this,args);}如果订阅者在if之后但在实际调用之前取消订阅,您将得到一个NullReferenceException。实现此功能的惯用方法是:私有OnCollectionChanged(NotifyCollectionChangedEventArgs args) { var处理程序= CollectionChanged;if (处理程序!=空)处理程序(此,args);}注意到这仍然会在订阅方造成问题,因为它可能在取消订阅后才被调用,但应该在那里处理。
  4. 调用事件处理程序是同步进行的。因此,如果订阅了CollectionChanged事件,那么在引发事件的每个操作中都将同步调用已注册的事件处理程序。这并不是完全令人惊讶的是,这会稍微减慢操作的速度。然而,在未观察到的情况下,情况不应该有太大的不同。
  5. 要真正找到问题所在,您必须使用分析器。
票数 8
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/30708

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档