首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >优化C#代码片段、ObservableCollection和AddRange

优化C#代码片段、ObservableCollection和AddRange
EN

Stack Overflow用户
提问于 2016-02-24 08:52:00
回答 5查看 1.6K关注 0票数 1

我正在分析由其他人编写的silverlight组件。我发现了很多热点和瓶颈,现在我遇到了这样的一个:

代码语言:javascript
运行
复制
public static class CollectionExtensions
{
    public static void AddRange<T>(this ObservableCollection<T> collection, IEnumerable<T> items)
    {
        foreach (var item in items)
        {
            collection.Add(item);
        }
    }
}

当然,这个扩展方法将AddRange方法添加到ObservableCollection中,但是它的计算非常密集。有没有人有一个更好的实现,或任何建议,如何提高这一段的性能?

谢谢

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2016-02-24 09:11:10

多次调用Add会导致多次引发INotifyCollectionChanged,这通常会导致UI重新绘制自身。

虽然Lee的回答在技术上是正确的,即一旦添加了所有项,引发Reset事件就是正确的方法,但我从经验中发现,许多网格控件(例如)并不积极支持Reset事件。

最普遍支持的选项是在ObservableCollection之外修改集合并重新创建ObservableCollection属性本身。

换句话说,在VM上定义如下的ObservableCollection .

代码语言:javascript
运行
复制
private ObservableCollection<MyItem> _items;
public ObservableCollection<MyItem> Items {
    get { return _items;}
    set 
    { 
        _items = value;
        OnPropertyChanged(()=> Items);
    }
}

...add您的新项目如下..。

代码语言:javascript
运行
复制
var tempColl = _items.ToList();
tempColl.AddRange(newItems);
Items = new ObservableCollection(tempColl);

关于这一技术,要记住的另一件事是,它是线程安全的,因为如果您重新创建ObservableCollection,可以从后台线程向ObservableCollection添加项。普通ObservableCollection不能通过非Dispatcher线程的Add方法向其添加项。

票数 5
EN

Stack Overflow用户

发布于 2016-02-24 09:02:22

这里的成本通常是由于每个单独添加的更改通知引起的。更可取的做法是创建一个为接受数据范围而优化的新集合实现。您可以添加所有值,然后引发单个事件,而不是为每个更改引发更改通知,然后绑定引擎将每个更改处理为单个更新。这个事件可以是一个大锤子,也可以是一个Reset,或者您可以提供更改的项,以及它们更改的索引。

这是一个在其Reset方法上使用单个AddRange通知的示例:

代码语言:javascript
运行
复制
/// <summary>
/// An implementation of <seealso cref="ObservableCollection{T}"/> that provides the ability to suppress
/// change notifications. In sub-classes that allows performing batch work and raising notifications 
/// on completion of work. Standard usage takes advantage of this feature by providing AddRange method.
/// </summary>
/// <typeparam name="T">The type of elements in the list.</typeparam>
public class ObservableList<T> : ObservableCollection<T>
{
    #region Fields
    private readonly Queue<PropertyChangedEventArgs> _notifications = new Queue<PropertyChangedEventArgs>();
    private readonly Queue<NotifyCollectionChangedEventArgs> _collectionNotifications = new Queue<NotifyCollectionChangedEventArgs>();
    private int _notificationSupressionDepth;
    #endregion

    public ObservableList()
    {
    }
    public ObservableList(IEnumerable<T> collection)
        : base(collection)
    {
    }

    public void AddRange(IEnumerable<T> list)
    {
        using (SupressNotifications())
        {
            foreach (var item in list)
            {
                Add(item);
            }
        }
        OnPropertyChanged("Count");
        OnPropertyChanged("Item[]");
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    public void RemoveRange(IEnumerable<T> list)
    {
        using (SupressNotifications())
        {
            foreach (var item in list)
            {
                Remove(item);
            }
        }
        OnPropertyChanged("Count");
        OnPropertyChanged("Item[]");
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    public void ReplaceRange(IEnumerable<T> list)
    {
        using (SupressNotifications())
        {
            Clear();
            foreach (var item in list)
            {
                Add(item);
            }
        }
        OnPropertyChanged("Count");
        OnPropertyChanged("Item[]");
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }


    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (_notificationSupressionDepth == 0)
        {
            base.OnCollectionChanged(e);
        }
        else
        {
            //We cant filter duplicate Collection change events as this will break how UI controls work. -LC
            _collectionNotifications.Enqueue(e);
        }
    }

    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (_notificationSupressionDepth == 0)
        {
            base.OnPropertyChanged(e);
        }
        else
        {
            if (!_notifications.Contains(e, NotifyEventComparer.Instance))
            {
                _notifications.Enqueue(e);
            }
        }
    }

    protected void OnPropertyChanged(string propertyName)
    {
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    protected IDisposable QueueNotifications()
    {
        _notificationSupressionDepth++;
        return Disposable.Create(() =>
                                     {
                                         _notificationSupressionDepth--;
                                         TryNotify();
                                     });
    }

    protected IDisposable SupressNotifications()
    {
        _notificationSupressionDepth++;
        return Disposable.Create(() =>
        {
            _notificationSupressionDepth--;
        });
    }

    private void TryNotify()
    {
        if (_notificationSupressionDepth == 0)
        {
            while (_collectionNotifications.Count > 0)
            {
                var collectionNotification = _collectionNotifications.Dequeue();
                base.OnCollectionChanged(collectionNotification);
            }

            while (_notifications.Count > 0)
            {
                var notification = _notifications.Dequeue();
                base.OnPropertyChanged(notification);
            }
        }
    }
}

编辑:添加缺少的NotifyEventComparer类和一个示例Disposable.Create方法

代码语言:javascript
运行
复制
public sealed class NotifyEventComparer : IEqualityComparer<PropertyChangedEventArgs>
{
    public static readonly NotifyEventComparer Instance = new NotifyEventComparer();

    bool IEqualityComparer<PropertyChangedEventArgs>.Equals(PropertyChangedEventArgs x, PropertyChangedEventArgs y)
    {
        return x.PropertyName == y.PropertyName;
    }

    int IEqualityComparer<PropertyChangedEventArgs>.GetHashCode(PropertyChangedEventArgs obj)
    {
        return obj.PropertyName.GetHashCode();
    }
}

//Either use Rx to access Disposable.Create or this simple implementation will do
public static class Disposable
{
    public static IDisposable Create(Action dispose)
    {
        if (dispose == null)
            throw new ArgumentNullException("dispose");

        return new AnonymousDisposable(dispose);
    }

    private sealed class AnonymousDisposable : IDisposable
    {
        private Action _dispose;

        public AnonymousDisposable(Action dispose)
        {
            _dispose = dispose;
        }

        public void Dispose()
        {
            var dispose = Interlocked.Exchange(ref _dispose, null);
            if (dispose != null)
            {
                dispose();
            }
        }
    }
}
票数 1
EN

Stack Overflow用户

发布于 2016-02-24 09:11:30

这是因为每次将项添加到集合时,ObservableCollection都会触发PropertyChanged事件。防止在批量添加项时触发此事件是您要查看的内容。这是一个优雅的解决方案,尽管我自己还没有尝试过。

https://peteohanlon.wordpress.com/2008/10/22/bulk-loading-in-observablecollection/

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35597364

复制
相关文章

相似问题

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