【问题标题】:Calculation on a realtime changing collection ( in c#)计算实时变化的集合(在 C# 中)
【发布时间】:2014-07-15 14:12:50
【问题描述】:

我有一个不断变化的对象集合(在集合中添加/删除以及属性更改也是可能的)。

我想通过对此集合的一些计算来获得一个新集合(对具有相同字段值的所有对象的某些字段进行求和/乘法)。并将计算列表绑定到 UI。

例如:所以我有几个水果的不同质量的比率和数量列表,现在我想找到一个新列表,其中包含每个水果的平均比率和总数量(无论质量如何),因为比率和数量可以运行时会发生变化,并且可以将更多品质和水果种类添加到集合中。

现在,如果在运行时第一个列表中的费率或数量发生变化,则第二个列表中的更改也应反映。

考虑到我们将列表绑定到数据网格,请建议应该使用哪些方法来实现它。

【问题讨论】:

  • LINQ 应该是你的朋友 - GroupBy、Sum 和 Average 方法。
  • 你有没有尝试过?我们能看到任何代码吗?
  • 在每次更改时一次又一次地进行所有计算是一个明智的想法吗?这会导致性能下降。在我的情况下,我在第一个列表中有 100-400 个项目和另外 4 个正在更改的属性。因此,如果我对每次更改进行此计算,则计算的平均频率为 40-50。
  • @micheal: groupBy(Name) & 然后聚合所有这些组,但这太慢了。
  • 我订阅了 notifypropertychanged 和 calculationchange 并且每次都触发 linq 查询。有没有办法控制这些计算时间 - 比如说在每 50 毫秒后运行 linq 查询,如果可能的话在后台运行,这样 UI 就不会冻结。

标签: c# wpf linq system.reactive xamdatagrid


【解决方案1】:

有一个名为 Buffer 的简洁的响应式扩展:

var o = new ObservableCollection<long>();
var s1 = Observable.Interval(TimeSpan.FromMilliseconds(100)).Subscribe(o.Add);
var s2 =
    Observable.FromEventPattern<NotifyCollectionChangedEventArgs>(o, "CollectionChanged")
        .Buffer(TimeSpan.FromMilliseconds(500))
        .Subscribe(
            s =>
            {
                Console.WriteLine("Last received {0}. Current count: {1}", Convert.ToInt64(s.Last().EventArgs.NewItems[0]), o.Count);                            
            });

这将允许您在发生时接收集合更改事件,但忽略除最后一个之外的所有事件。

更新 1

当项目更新时CollectionChanged 不会触发是一个已知问题。 MSDN 文档根本不正确。您必须实现TrulyObservableCollection,但您的数据元素也必须实现INotifyChanged 接口。

namespace ConsoleApplication1
{
    #region

    using System;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Linq;
    using System.Reactive.Linq;
    using System.Threading;

    #endregion

    internal class Program
    {
        #region Methods

        private static void Main(string[] args)
        {
            var autoReset = new AutoResetEvent(false);
            var r = new Random();
            var o = new TrulyObservableCollection<DataPoint>();
            var subscription1 = Observable.Interval(TimeSpan.FromSeconds(1)).Take(3).Subscribe(
                i =>
                {
                    o.Add(
                        new DataPoint
                        {
                            ItemCount = r.Next(100)
                        });
                    Console.WriteLine("Fire1 {0}", i);
                });
            var subscription2 =
                Observable.FromEventPattern<NotifyCollectionChangedEventArgs>(o, "CollectionChanged")
                    .Subscribe(s => { Console.WriteLine("List changed. Current total {0}", o.Sum(s1 => s1.ItemCount)); });
            var subscription3 = Observable.Interval(TimeSpan.FromSeconds(1)).Delay(TimeSpan.FromSeconds(3)).Take(3).Finally(
                () =>
                {
                    o.Clear();
                    autoReset.Set();
                }).Subscribe(
                    i =>
                    {
                        if (o.Any())
                        {
                            o[r.Next(o.Count)].ItemCount = r.Next(100);
                            Console.WriteLine("Fire3 {0}", i);
                        }
                    });
            autoReset.WaitOne();
        }

        #endregion

        public class TrulyObservableCollection<T> : ObservableCollection<T>
            where T : INotifyPropertyChanged
        {
            #region Constructors and Destructors

            public TrulyObservableCollection() { CollectionChanged += this.TrulyObservableCollection_CollectionChanged; }

            #endregion

            #region Methods

            private void TrulyObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                if (e.NewItems != null)
                {
                    foreach (Object item in e.NewItems)
                    {
                        (item as INotifyPropertyChanged).PropertyChanged += this.item_PropertyChanged;
                    }
                }
                if (e.OldItems != null)
                {
                    foreach (Object item in e.OldItems)
                    {
                        (item as INotifyPropertyChanged).PropertyChanged -= this.item_PropertyChanged;
                    }
                }
            }

            private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                var a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
                OnCollectionChanged(a);
            }

            #endregion
        }

        private class DataPoint : INotifyPropertyChanged
        {
            #region Fields

            private int itemCount;

            #endregion

            #region Public Events

            public event PropertyChangedEventHandler PropertyChanged;

            #endregion

            #region Public Properties

            public int ItemCount
            {
                get { return itemCount; }
                set
                {
                    itemCount = value;
                    this.OnPropertyChanged("ItemCount");
                }
            }

            #endregion

            #region Methods

            protected virtual void OnPropertyChanged(string propertyName = null)
            {
                var handler = PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(propertyName));
                }
            }

            #endregion
        }
    }
}

【讨论】:

  • 以及如何订阅Rx中每一项的属性变化?
  • ObservableCollection.CollectionChanged 事件 - 在添加、删除、更改、移动或刷新整个列表时发生。
  • 然而,根据我的测试,当项目被修改时,它似乎并没有触发,尽管文档说了什么。
  • 要记住的一件事......即使集合没有更新,Buffer 扩展也会继续触发,这让我很吃惊。一旦预计不再有更新,您可能希望取消订阅,例如,如果更新项目的数量在某个预定义的时间内保持为 0。
  • 还有一个问题:当使用 args.Reset 触发 OnCollectionChanged 事件时,它在 e.NewItems 或 e.OldItems 中不提供任何内容(在重置的情况下均为 null),那么我如何知道哪些对象已更改。 PS:在我的情况下,清除整个列表并再次添加所有项目将是非常昂贵的操作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-16
  • 1970-01-01
相关资源
最近更新 更多