发布时间:2026/8/25 3:59:28
AvaloniaUI 中 Observable 与 ObserveOn 的用法和区别 1. Observable 基础概念在 AvaloniaUI 和 ReactiveUI 框架中Observable可观察序列是响应式编程的核心。它代表一个随时间推移的数据流可以被订阅以接收数据更新。1.1 Observable 的基本用法using System; using System.Reactive.Linq; // 创建一个简单的 Observable var observable Observable.Return(Hello, Avalonia!); // 订阅 Observable var subscription observable.Subscribe( value Console.WriteLine($收到值: {value}), error Console.WriteLine($发生错误: {error}), () Console.WriteLine(序列完成) ); // 创建间隔 Observable每秒发射一个值 var timerObservable Observable.Interval(TimeSpan.FromSeconds(1)) .Take(5); // 只取前5个值 timerObservable.Subscribe( tick Console.WriteLine($Tick: {tick}) );1.2 在 AvaloniaUI 中创建 Observable 属性using ReactiveUI; using System.Reactive.Linq; public class UserViewModel : ReactiveObject { private string _name; [Reactive] public string Name { get _name; set this.RaiseAndSetIfChanged(ref _name, value); } // 创建一个基于 Name 属性的 Observable public IObservablestring NameObservable this.WhenAnyValue(x x.Name); // 创建一个过滤后的 Observable只包含长度大于3的名称 public IObservablestring ValidNameObservable this.WhenAnyValue(x x.Name) .Where(name !string.IsNullOrEmpty(name) name.Length 3); public UserViewModel() { // 订阅 Name 变化 NameObservable.Subscribe(newName { Console.WriteLine($用户名已更新: {newName}); }); // 订阅有效名称变化 ValidNameObservable.Subscribe(validName { Console.WriteLine($有效用户名: {validName}); }); } }2. ObserveOn 的作用与用法ObserveOn是一个操作符用于指定 Observable 序列在哪个调度器Scheduler上执行观察者订阅者的回调。在 UI 开发中这通常用于确保 UI 更新在正确的线程上执行。2.1 ObserveOn 的基本语法using System.Reactive.Concurrency; using System.Reactive.Linq; // 创建 Observable var source Observable.Interval(TimeSpan.FromSeconds(1)); // 使用 ObserveOn 指定调度器 var uiObservable source .ObserveOn(RxApp.MainThreadScheduler) // 在 UI 线程观察 .Select(x $UI线程更新: {x}); var backgroundObservable source .ObserveOn(Scheduler.Default) // 在后台线程观察 .Select(x $后台线程处理: {x});2.2 在 AvaloniaUI 中使用 ObserveOnusing Avalonia.Threading; using ReactiveUI; using System.Reactive.Concurrency; using System.Reactive.Linq; public class DataViewModel : ReactiveObject { private string _status; private int _progress; [Reactive] public string Status { get _status; set this.RaiseAndSetIfChanged(ref _status, value); } [Reactive] public int Progress { get _progress; set this.RaiseAndSetIfChanged(ref _progress, value); } public DataViewModel() { // 模拟后台数据流 var dataStream Observable.Interval(TimeSpan.FromMilliseconds(100)) .Take(100) // 取100个值 .Select(x (int)x * 10); // 转换为进度值 // 错误示例直接在后台线程更新 UI 属性可能导致跨线程异常 // dataStream.Subscribe(progress Progress progress); // 正确示例使用 ObserveOn 确保在 UI 线程更新 dataStream .ObserveOn(RxApp.MainThreadScheduler) // 关键指定 UI 线程 .Subscribe( progress { Progress progress; Status $处理中: {progress}%; }, error Status $错误: {error.Message}, () Status 处理完成 ); } // 另一种方式使用 Dispatcher public async Task LoadDataAsync() { var dataStream Observable.Interval(TimeSpan.FromMilliseconds(50)) .Take(50) .Select(x (int)x * 2); await dataStream .ObserveOn(RxApp.MainThreadScheduler) .ForEachAsync(progress { Progress progress; }); } }3. Observable 与 ObserveOn 的核心区别特性ObservableObserveOn本质数据源/数据流调度器操作符作用定义数据如何产生和发射定义数据在哪个线程被消费使用时机创建数据流时订阅数据流时线程影响定义数据产生的线程SubscribeOn定义数据观察的线程典型场景属性变更流、定时器、事件转换UI更新、线程切换、避免跨线程异常3.1 SubscribeOn vs ObserveOn理解两者的区别对于正确使用 Reactive Extensions 至关重要using System.Reactive.Concurrency; using System.Reactive.Linq; // SubscribeOn控制 Observable 的执行上下文数据产生的线程 var source1 Observable.Createint(observer { Console.WriteLine($Create 在线程: {Environment.CurrentManagedThreadId}); observer.OnNext(1); observer.OnCompleted(); return System.Reactive.Disposables.Disposable.Empty; }) .SubscribeOn(NewThreadScheduler.Default); // 在新线程执行 Create // ObserveOn控制观察者的执行上下文数据消费的线程 var source2 Observable.Return(2) .ObserveOn(RxApp.MainThreadScheduler); // 在 UI 线程消费 // 组合使用 var combined Observable.Interval(TimeSpan.FromSeconds(1)) .SubscribeOn(ThreadPoolScheduler.Instance) // 在线程池产生数据 .ObserveOn(RxApp.MainThreadScheduler) // 在 UI 线程消费数据 .Select(x $值: {x});4. 实际应用示例4.1 实时搜索框实现using ReactiveUI; using System.Reactive.Linq; public class SearchViewModel : ReactiveObject { private string _searchText; private Liststring _results; [Reactive] public string SearchText { get _searchText; set this.RaiseAndSetIfChanged(ref _searchText, value); } [Reactive] public Liststring Results { get _results; set this.RaiseAndSetIfChanged(ref _results, value); } public SearchViewModel() { // 创建搜索文本的 Observable var searchObservable this.WhenAnyValue(x x.SearchText) .Throttle(TimeSpan.FromMilliseconds(300)) // 防抖300ms内只取最后一次 .DistinctUntilChanged() // 去重值变化时才触发 .Where(text !string.IsNullOrWhiteSpace(text)) .Select(text text.Trim()); // 订阅搜索 Observable在后台线程执行搜索在 UI 线程更新结果 searchObservable .SelectMany(async text { // 模拟异步搜索在后台线程执行 await Task.Delay(100); return SearchDatabase(text); }) .ObserveOn(RxApp.MainThreadScheduler) // 切回 UI 线程更新结果 .Subscribe(results { Results results; }); } private Liststring SearchDatabase(string keyword) { // 模拟数据库搜索 var data new Liststring { Apple, Banana, Cherry, Date, Elderberry, Fig, Grape, Honeydew, Kiwi, Lemon }; return data .Where(item item.Contains(keyword, StringComparison.OrdinalIgnoreCase)) .ToList(); } }4.2 多数据源合并与线程管理using ReactiveUI; using System.Reactive.Linq; public class DashboardViewModel : ReactiveObject { private string _cpuUsage; private string _memoryUsage; private string _networkStatus; [Reactive] public string CpuUsage { get _cpuUsage; set this.RaiseAndSetIfChanged(ref _cpuUsage, value); } [Reactive] public string MemoryUsage { get _memoryUsage; set this.RaiseAndSetIfChanged(ref _memoryUsage, value); } [Reactive] public string NetworkStatus { get _networkStatus; set this.RaiseAndSetIfChanged(ref _networkStatus, value); } public DashboardViewModel() { // 创建多个数据源 Observable var cpuObservable Observable.Interval(TimeSpan.FromSeconds(2)) .Select(_ GetCpuUsage()); var memoryObservable Observable.Interval(TimeSpan.FromSeconds(3)) .Select(_ GetMemoryUsage()); var networkObservable Observable.Interval(TimeSpan.FromSeconds(5)) .Select(_ GetNetworkStatus()); // 合并所有 Observable统一在 UI 线程更新 Observable.Merge( cpuObservable.Select(usage new { Type CPU, Value usage }), memoryObservable.Select(usage new { Type Memory, Value usage }), networkObservable.Select(status new { Type Network, Value status }) ) .ObserveOn(RxApp.MainThreadScheduler) // 统一切换到 UI 线程 .Subscribe(data { switch (data.Type) { case CPU: CpuUsage data.Value; break; case Memory: MemoryUsage data.Value; break; case Network: NetworkStatus data.Value; break; } }); } private string GetCpuUsage() ${new Random().Next(10, 90)}%; private string GetMemoryUsage() ${new Random().Next(30, 95)}%; private string GetNetworkStatus() new Random().Next(0, 2) 0 ? 在线 : 离线; }5. 常见问题与最佳实践5.1 常见错误忘记使用 ObserveOn在后台线程更新 UI 属性导致跨线程异常过度使用 ObserveOn不必要的线程切换影响性能ObserveOn 位置错误在错误的位置调用 ObserveOn 可能导致逻辑错误5.2 最佳实践UI 更新必须使用 ObserveOn(RxApp.MainThreadScheduler)耗时操作使用 SubscribeOn 切换到后台线程合理使用 Throttle 和 DistinctUntilChanged 减少不必要的更新及时清理订阅避免内存泄漏使用 WhenAnyValue 创建属性变更 Observable5.3 性能优化示例// 优化前频繁更新导致性能问题 this.WhenAnyValue(x x.SearchText) .Subscribe(text UpdateResults(text)); // 每次按键都触发 // 优化后使用防抖和过滤 this.WhenAnyValue(x x.SearchText) .Throttle(TimeSpan.FromMilliseconds(300)) // 防抖 .DistinctUntilChanged() // 去重 .Where(text text?.Length 3) // 过滤短文本 .ObserveOn(RxApp.MainThreadScheduler) // UI 线程更新 .Subscribe(text UpdateResults(text));6. 总结Observable和ObserveOn是 AvaloniaUI 响应式编程的两个核心概念Observable是数据流的抽象用于表示随时间变化的数据序列ObserveOn是调度器操作符用于控制数据在哪个线程被消费在 UI 开发中ObserveOn(RxApp.MainThreadScheduler)是确保线程安全的必要手段正确理解和使用这两个概念可以编写出更简洁、更安全、更高效的响应式 UI 代码通过合理组合 Observable 的各种操作符如 Select、Where、Throttle、Merge 等和正确的线程调度可以在 AvaloniaUI 中构建出响应迅速、代码清晰的数据驱动界面。上周忘记了这周加一篇

相关新闻

2026/8/25 3:59:28

Unity 2D飞行棋游戏开发实战:从核心逻辑到打包发布

这次我们来看一个完整的 Unity 2D 飞行棋游戏开发实战项目。这不是一个简单的概念演示,而是一个从零开始,涵盖游戏核心逻辑、UI交互、动画效果、音效管理到最终打包发布的完整项目。对于想通过一个具体案例掌握 Unity 2D 开发全流程的开发者来说&#xf…

2026/8/25 3:59:28

AI编程技能集实战:如何用结构化提示词打造专属编程助手

这次我们来看一个关于 AI 编程技能集(Skills)的实测项目。核心不是讨论某个具体的开源代码仓库,而是聚焦于一个由知名开发者 Matt Pocock 提出并推广的 AI 编程方法论——“Skills”。这个概念在 Theo(t3.gg)的视频中被…

2026/8/25 6:14:36

从聊天到API:DeepSeek实战指南与工程化应用

上周,我临时需要处理一批文档摘要任务。手头一个常用的在线工具突然限速,另一个本地部署的模型又因为显存不足卡在了半路。情急之下,我翻出了之前收藏的一个DeepSeek API调用脚本,想着用它应个急。结果,脚本跑起来&…

2026/8/25 6:14:36

先进封装公司哪个好选型指南与避坑要点

存储器模组封装企业采购真空回流炉,微电子工艺研究院器件封装实验室采购真空共晶炉的秘密武器 嘿,小伙伴们,你们知道吗?在我们日常使用的电子产品中,那些小巧的芯片是如何被“穿衣服”的吗?🤔 是…

2026/8/25 6:14:36

AI驱动开发与创作:从Cursor Origin到Grok大赛的技术实践

这次我们来看三个近期值得关注的技术动态:特斯拉的无人驾驶出租车 Cybercab 即将在奥斯汀亮相,Cursor 推出了名为 Origin 的 Agent 级代码托管平台,以及 Grok 悬赏 17.5 万美元举办 AI 电影大赛。这三个事件分别代表了自动驾驶、AI 编程工具和…

2026/8/25 6:14:36

UPS不间断电源选购全攻略:从原理到实践,16款高性价比产品推荐

最近在帮朋友配置家庭办公室和工作室时,发现一个普遍被忽视但至关重要的设备——UPS(不间断电源)。无论是深夜赶稿时突然断电导致文档丢失,还是NAS里的珍贵数据因电压不稳而损坏,一次意外就足以让人追悔莫及。尤其对于…

2026/8/25 1:04:19

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/24 1:12:32

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/24 8:17:29

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/25 0:04:14

三步把QQ空间历史说说导出到本地:GetQzonehistory 极简指南

三步把QQ空间历史说说导出到本地:GetQzonehistory 极简指南 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory Meta Description:GetQzonehistory 是一个QQ空间历史说…

2026/8/25 0:04:14

洛谷 P7912:[CSP-J 2021 T4] 小熊的果篮 ← 双向链表

【题目来源】 https://www.luogu.com.cn/problem/P7912 【题目描述】 小熊的水果店里摆放着一排 n 个水果。每个水果只可能是苹果或桔子,从左到右依次用正整数 1,2,…,n 编号。连续排在一起的同一种水果称为一个“块”。小熊要把这一排水果挑到若干个果篮里&#x…

2026/8/24 13:42:17

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/24 18:13:48

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/25 1:08:14

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…