ObservableCollection和List的区别以及ObservableCollection 异步调用问题
ObservableCollection和List的区别总结区别它们,最简单的方法就是看看他们继承的类和接口,还有它们的方法。(学习有技巧,会让你事半功倍,效率提升。)学习ObservableCollection比较简单,继承了Collection, INotifyCollectionChanged, INotifyPropertyChangedspaCollection:为泛型集合提供基类。co
ObservableCollection和List的区别总结
区别它们,最简单的方法就是看看他们继承的类和接口
,还有它们的方法。
- ObservableCollection比较简单,继承了 Collection, INotifyCollectionChanged, INotifyPropertyChanged
observable 能看得到的; 能察觉到的;值得注意的东西;感觉到[看得见]的事物;【物】可观察量;观察算符
观察者模式是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实时事件处理系统。 — 维基百科
观察者模式又叫发布订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。
我们可以使用日常生活中,期刊订阅的例子来形象地解释一下上面的概念。期刊订阅包含两个主要的角色:期刊出版方和订阅者,他们之间的关系如下:
- 期刊出版方 - 负责期刊的出版和发行工作
- 订阅者 - 只需执行订阅操作,新版的期刊发布后,就会主动收到通知,如果取消订阅,以后就不会再收到通知
在观察者模式中也有两个主要角色:Subject (主题) 和 Observer (观察者)
它们分别对应例子中的期刊出版方和订阅者。接下来我们来看张图,从而加深对上面概念的理解。
- Collection:为泛型集合提供基类。code
- INotifyCollectionChanged:将集合的动态更改通知给侦听器,例如,什么时候添加和移除项或者重置整个集合对象。对象
- INotifyPropertyChanged:向客户端发出某一属性值已更改的通知。排序
因此在ObservableCollection这个类的方法,对数据的操做不多,重点放在了当本身本事变化的时候(无论是属性,仍是集合)会调用发出通知的事件。(通常用于更新UI,固然也能够用于写其余的事情。这个之后会写)继承 List就比较多了,继承了IList, ICollection, IEnumerable, IList, ICollection, IEnumerable。索引
- IList:表示可按照索引单独访问的一组对象:接口
- ICollection:定义操做泛型集合的方法:事件
- IEnumerable:公开枚举器,该枚举器支持在指定类型的集合上进行简单迭代:get
- IList:表示可按照索引单独访问的对象的非泛型集合。
- ICollection:定义全部非泛型集合的大小、枚举器和同步方法。
- IEnumerable:公开枚举器,该枚举器支持在非泛型集合上进行简单迭代。
而后随便作个demo看看效果。
<ListBox x:Name="listbind" Height="61" HorizontalAlignment="Left" Margin="146,12,0,0" VerticalAlignment="Top" Width="120" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox x:Name="observbind" Height="74" HorizontalAlignment="Left" Margin="146,111,0,0" VerticalAlignment="Top" Width="120" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock Height="23" HorizontalAlignment="Left" Margin="38,58,0,0" Name="textBlock1" Text="List绑定数据" VerticalAlignment="Top" />
<TextBlock Height="44" HorizontalAlignment="Left" Margin="12,125,0,0" Name="textBlock2" Text="ObservableCollection绑定数据" VerticalAlignment="Top" Width="112" />
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="77,211,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
xaml页面很简单,托2个listbox分别用来绑定ObservableCollection和List
public class Person
{
public string Name { get; set; }
}
实体类。
private List<Person> person1 = new List<Person>();
private ObservableCollection<Person> person2 = new ObservableCollection<Person>();
public DemoTestDiff()
{
InitializeComponent();
person1.Add(new Person() { Name = "张三" });
person1.Add(new Person() { Name = "李四" });
listbind.ItemsSource = person1;
person2.Add(new Person() { Name = "张三" });
person2.Add(new Person() { Name = "李四" });
observbind.ItemsSource = person2;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
person1.Add(new Person() { Name = "王五" });
person2.Add(new Person() { Name = "王五" });
}
运行程序点击button按钮
而后只有ObservableCollection的有添加。表示当集合对象的集合改变时,只有ObservableCollection会发出通知更新UI。 这只是他们两个区别之一。
综上所述:
ObservableCollection表示一个动态数据集合,在添加项、移除项或刷新整个列表时,此集合将提供通知。
List表示可经过索引访问的对象的强类型列表。提供用于对列表进行搜索、排序和操做的方法。(大部分操做用Linq,很强大也很方便。)
问题介绍
当ObservableCollection列表被UI线程占用时,若是在异步线程中调用ObservableCollection,会弹出如下异常:
问题分析
咱们使用一个viewModel,在ViewModel中添加ObservableCollection类型的ItemsSource列表。
异步在列表使用ListBox绑定ItemsSource列表。再由界面触发对ItemsSource的修改。
1 public class ViewModel : INotifyPropertyChanged
2 {
3 private ObservableCollection<string> _itemsSource = new ObservableCollection<string>();
4
5 public ObservableCollection<string> ItemsSource
6 {
7 get => _itemsSource;
8 set
9 {
10 _itemsSource = value;
11 OnPropertyChanged();
12 }
13 }
14
15 public event PropertyChangedEventHandler PropertyChanged;
16
17 [NotifyPropertyChangedInvocator]
18 protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
19 {
20 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
21 }
22 }
View Code
- 直接在异步线程下修改ObservableCollection–报错this
private void ButtonBase_OnClick(object sender, RoutedEventArgs e) 2 { 3 var viewModel = this.DataContext as ViewModel; 4 Task.Run(() => 5 { 6 //此段调用异常 7 viewModel.ItemsSource.Add("test1"); 8 }); 9 }
- 在异步线程下,赋值ObservableCollection–正常spa
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
2 {
3 var viewModel = this.DataContext as ViewModel;
4 Task.Run(() =>
5 {
6 //此段不会报错
7 var list = viewModel.ItemsSource.ToList();
8 list.Add("test0");
9 viewModel.ItemsSource = new ObservableCollection<string>(list);
10 });
11 }
- 在异步线程下,赋值ObservableCollection后,再修改ObservableCollection–正常
5. ` private void Button1_OnClick(object sender, RoutedEventArgs e)
2 {
3 var viewModel = this.DataContext as ViewModel;
4 Task.Run(() =>
5 {
6 //此段不会报错
7 viewModel.ItemsSource = new ObservableCollection<string>(new List<string>() { "test3", "test2" });
8 //此段不会报错
9 viewModel.ItemsSource.Add("test4");
10 });
11 }`
在异步线程下设置的ItemsSource,能够被UI线程调用。此处能够理解为,赋值时,框架默默转到UI线程处理了?get
可是上面3流程,为什么正常,so weird~string
- 异步线程下,回到UI线程中,修改ObservableCollection–正常
6. private void Button1_OnClick(object sender, RoutedEventArgs e)
2 {
3 var viewModel = this.DataContext as ViewModel;
4 Task.Run(() =>
5 {
6 Application.Current.Dispatcher.Invoke(() =>
7 {
8 //此段不会报错
9 viewModel.ItemsSource.Add("test");
10 });
11 });
12 }
在进行WPF开发过程当中,须要从一个新的线程中操做ObservableCollection,结果程序抛出一个NotSupportedException的错误ide
public class AsyncObservableCollection<T> : ObservableCollection<T>
{
//获取当前线程的SynchronizationContext对象
private SynchronizationContext _synchronizationContext = SynchronizationContext.Current;
public AsyncObservableCollection() { }
public AsyncObservableCollection(IEnumerable<T> list) : base(list) { }
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (SynchronizationContext.Current == _synchronizationContext)
{
//若是操做发生在同一个线程中,不须要进行跨线程执行
RaiseCollectionChanged(e);
}
else
{
//若是不是发生在同一个线程中
//准确说来,这里是在一个非UI线程中,须要进行UI的更新所进行的操做
_synchronizationContext.Post(RaiseCollectionChanged, e);
}
}
private void RaiseCollectionChanged(object param)
{
// 执行
base.OnCollectionChanged((NotifyCollectionChangedEventArgs)param);
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (SynchronizationContext.Current == _synchronizationContext)
{
// Execute the PropertyChanged event on the current thread
RaisePropertyChanged(e);
}
else
{
// Post the PropertyChanged event on the creator thread
_synchronizationContext.Post(RaisePropertyChanged, e);
}
}
private void RaisePropertyChanged(object param)
{
// We are in the creator thread, call the base implementation directly
base.OnPropertyChanged((PropertyChangedEventArgs)param);
}
}
观察者模式实战
Subject 类定义:
class Subject {
constructor() {
this.observerCollection = [];
}
registerObserver(observer) {
this.observerCollection.push(observer);
}
unregisterObserver(observer) {
let index = this.observerCollection.indexOf(observer);
if(index >= 0) this.observerCollection.splice(index, 1);
}
notifyObservers() {
this.observerCollection.forEach((observer)=>observer.notify());
}
}
Observer 类定义:
class Observer {
constructor(name) {
this.name = name;
}
notify() {
console.log(`${this.name} has been notified.`);
}
}
使用示例:
let subject = new Subject(); // 创建主题对象
let observer1 = new Observer('semlinker'); // 创建观察者A - 'semlinker'
let observer2 = new Observer('lolo'); // 创建观察者B - 'lolo'
subject.registerObserver(observer1); // 注册观察者A
subject.registerObserver(observer2); // 注册观察者B
subject.notifyObservers(); // 通知观察者
subject.unregisterObserver(observer1); // 移除观察者A
subject.notifyObservers(); // 验证是否成功移除
Pull vs Push
Pull 和 Push 是数据生产者和数据的消费者两种不同的交流方式。
什么是Pull?
在 “拉” 体系中,数据的消费者决定何时从数据生产者那里获取数据,而生产者自身并不会意识到什么时候数据将会被发送给消费者。
每一个 JavaScript 函数都是一个 "拉" 体系,函数是数据的生产者,
调用函数的代码通过 ''拉出" 一个单一的返回值来消费该数据。
const add = (a, b) => a + b;
let sum = add(3, 4);
什么是Push?
在 “推” 体系中,数据的生产者决定何时发送数据给消费者,消费者不会在接收数据之前意识到它将要接收这个数据。
Promise(承诺) 是当今 JS 中最常见的 “推” 体系,一个Promise (数据的生产者)发送一个 resolved value (成功状态的值)来执行一个回调(数据消费者),但是不同于函数的地方的是:Promise 决定着何时数据才被推送至这个回调函数。
RxJS 引入了 Observables (可观察对象),一个全新的 “推” 体系。一个可观察对象是一个产生多值的生产者,当产生新数据的时候,会主动 “推送给” Observer (观察者)。
生产者 消费者
pull拉 被请求的时候产生数据 决定何时请求数据
push推 按自己的节奏生产数据 对接收的数据进行处理
接下来我们来看张图,从而加深对上面概念的理解:
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)