反应式编程是一种相对于命令式的编程范式,由函数式的组合声明来构建异步数据流。要理解这个概念,可以简单的借助Excel中的单元格函数。

上图中,A1=B1+C1,无论B1和C1中的数据怎么变化,A1中的值都会自动变化,这其中就蕴含了反应式/响应式编程的思想。
反应式编程对于数据的处理不关心具体的数据值是多少,只要构建出数据的函数式处理,就能并行的异步处理数据流。
Reactive UI 是一种反应式编程的跨平台MVVM框架,支持Xamarin Forms、Xamarin.iOS、Xamarin.Android、Xamarin.Mac、Tizen、Windows Forms、WPF 和UWP。
本文对比经典的MVVM框架MVVMLight框架来展示ReactiveUI框架的特殊之处。
在MVVMLight中,依赖属性和命令的绑定一般都是放在Xaml中,并且大部分情况下不需要给控件定义Name属性。
| 1 |
|
这是属于弱绑定,在Reactive UI框架中也提供这样的弱绑定,但Reactive UI框架官方推荐使用后台强绑定方式。
在强绑定方式中,需要给控件定义他的Name属性。
| 1 |
|
在界面后台的cs文件中使用强绑定方式。
| 1 2 |
|
在Reactive UI框架中,提供了单向绑定和双向绑定两种绑定类型,上述代码中的OneWayBind是属于ViewModel->View的单向绑定,另外还有一个API Bind则是双向绑定。
this.Bind(ViewModel, vm => vm.BtnContent, vw => vw.btnOpenFile.Content);
之所以官方推荐这样的绑定方式,是因为框架中提供了一个管理ViewModel生命周期的API WhenActivated,解决了Xaml弱绑定方式带来的内存泄露的可能性。
在WhenActivated API的函数回调中进行绑定属性和Command,可以同步跟踪View和对应绑定属性的生命周期,避免发生内存泄露。
this.WhenActivated(dispos => {
this.OneWayBind(ViewModel, vm => vm.BtnContent, vw => vw.btnOpenFile.Content).DisposeWith(dispos);
});
WhenActivated 会在View被激活时同步调用注册的回调函数,注意,在OneWayBind后面新增了一个API调用DisposeWith,他可以确保当界面被销毁时,对应的viewModel及其绑定的属性和命令也会被销毁。
类似的,绑定Commond

this.WhenActivated(dispos => {
this.OneWayBind(ViewModel, vm => vm.BtnContent, vw => vw.btnOpenFile.Content).DisposeWith(dispos);
this.BindCommand(ViewModel,
viewModel => viewModel.OpenPage,
view => view.openButton)
.DisposeWith(disposableRegistration);
});

这样的强绑定相比于Xaml中的弱绑定,会有以下的优势:
1.提供了ViewModel的生命周期管理,避免内存泄露。
2.控件和后台属性的对应关系更为直观,提高代码的可阅读性。
当然也有一定的缺陷,会增加代码量,并且增加View和ViewModel的耦合性。
在MVVMLight中定义一个带通知的属性和Commond:

private string content ;
public string Content
{
get { return content; }
set
{
content = value;
RaisePropertyChanged(() => Content);
}
}
private RelayCommand openFileCommand = null;
public RelayCommand OpenFileCommand
{
get { return openFileCommand = openFileCommand ?? new RelayCommand(OpenFile); }
}

在ReactiveUI中也通成功了类似RaisePropertyChanged和RelayCommand功能的API,RaiseAndSetIfChanged和ReactiveCommand。

private string content;
public string Content
{
get { return content; }
set
{
this.RaiseAndSetIfChanged(ref content,value);
}
}
private ReactiveCommand<Unit, Unit> openFileCommand;
public ReactiveCommand<Unit, Unit> OpenFileCommand
{
get { return openFileCommand = openFileCommand ?? ReactiveCommand.Create(() => { }); }
}

其中ReactiveCommand的两个Unit,前一个是传入参数,后一个是返回值。ReactiveCommand的定义与MVVMLight大同小异。
但是在ReactiveUI中,还有更简单方便的定义可通知的属性,使用标记[Reactive]。
[Reactive]
public string Content { get; set; }
以上代码等价于

private string content;
public string Content
{
get { return content; }
set
{
this.RaiseAndSetIfChanged(ref content,value);
}
}

在.Net中,带通知功能的数据集合一般使用ObservableCollection,但是这个类存在一个限制,不支持多线程操作元素,只能在主线程中增加或者删除元素。所以在多线程操作ObservableCollection的时候,一般都需要通过Dispatcher或者线程上下文来推送操作到UI线程。
针对这个问题,ReativeUI框架提供了更优雅的操作方式,SourceList,SourceCache, ObservableCollectionExtended,都是线程安全的集合,需要和ReadOnlyObservableCollection一起搭配使用,用于创建可绑定的线程安全的数据集合。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
以一个读取磁盘文件夹信息的小功能为例。
一般都需要定义一个ObservableCollection的Model集合,在子线程中需要通过Dispatcher操作集合。

public ObservableCollection<FolderModel> FolderModels { get; set; }
private async Task LoadFolderInfoWithSelectedDiskChanged(string diskName)
{
await Task.Run(() => {
var files = Directory.GetDirectories(diskName);
foreach (var fileName in files)
{
FolderModel folderModel = new FolderModel();
DirectoryInfo directoryInfo = new DirectoryInfo(fileName);
folderModel.FolderName = directoryInfo.Name;
folderModel.CreateTime = directoryInfo.CreationTime;
_dispatcher.Invoke(() => {
FolderModels.Add(folderModel);
});
}
});
}

而在ReactiveUI 框架中,不需要Dispatcher这个东西,而是需要通过一个辅助类ObservableAsPropertyHelper。
ObservableAsPropertyHelper 是一个简化 IObservable 和 ViewModel 上的属性之间的互操作的类,为一个普通属性/字段和一个IObservable对象之间建立观察者模式的联系。
以上代码可以修改成:

//当前选中的磁盘符号,是一个IObservable对象
[Reactive]
public string SelectedDisk { get; set; }
//使用ObservableAsPropertyHelper包装
private readonly ObservableAsPropertyHelper<IEnumerable<FolderModel>> _folderModels;
//FolderModels可用于强绑定
public IEnumerable<FolderModel> FolderModels => _folderModels.Value;
//将_folderModels和SelectedDisk建立观察者和被观察者联系,构建函数组合式声明,当SelectedDisk改变时,
//会自动触发所注册的事件并自动给指定的属性FolderModels赋值。
_folderModels = this.WhenAnyValue(s => s.SelectedDisk)
.Where(s => !string.IsNullOrWhiteSpace(s))
.SelectMany(LoadFolderInfoWithSelectedDiskChanged)
.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, nameof(FolderModels));//将计算后得到的结果赋值到指定的属性中
private async Task<IEnumerable<FolderModel>> LoadFolderInfoWithSelectedDiskChanged(string diskName)
{
List<FolderModel> folderModels = new List<FolderModel>();
var files = Directory.GetDirectories(diskName);
foreach (var fileName in files)
{
FolderModel folderModel = new FolderModel();
DirectoryInfo directoryInfo = new DirectoryInfo(fileName);
folderModel.FolderName = directoryInfo.Name;
folderModel.CreateTime = directoryInfo.CreationTime;
folderModels.Add(folderModel);
}
//这个方法中不需要操作FolderModels 只需要把结果返回即可
await Task.CompletedTask;
return folderModels;
}

其中ObservableAsPropertyHelper包装的对象是可以任何对象,而LoadFolderInfoWithSelectedDiskChanged方法必须要带有结果返回的异步方法,这样就构成了函数式声明的异步数据流。
本文列了一些ReactiveUI的简单使用,下一篇会通过一个实例代码进一步学习ReactiveUI框架
我正在使用i18n从头开始构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在rubyonrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi
几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道rubyonrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim
网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现
Transformers开始在视频识别领域的“猪突猛进”,各种改进和魔改层出不穷。由此作者将开启VideoTransformer系列的讲解,本篇主要介绍了FBAI团队的TimeSformer,这也是第一篇使用纯Transformer结构在视频识别上的文章。如果觉得有用,就请点赞、收藏、关注!paper:https://arxiv.org/abs/2102.05095code(offical):https://github.com/facebookresearch/TimeSformeraccept:ICML2021author:FacebookAI一、前言Transformers(VIT)在图
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg