Rx框架之旅始于一个个人小项目。如今,几乎每天都在使用Rx框架,这要归功于一个让生活变得更轻松的小型个人框架。所以,今天没有长篇大论的文章,毕竟今天是周五,只是列举一下最喜欢的一些反应式框架扩展方法。这篇文章可以看作是之前那篇关于一些实用类的文章的续集。
当对某个视图模型的属性变更感兴趣时,经常需要编写如下的意大利面条代码:
public void UpdateUser(User user)
{
user.PropertyChanged += user_PropertyChanged;
NameChanged();
}
void user_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Name")
NameChanged();
}
private void NameChanged()
{
// Do wonderful stuff
}
将其替换为:
user
.ItemPropertyChanged(u => u.Name, true)
.Subscribe((args) =>
{
// Do wonderful stuff with args.NewValue and args.OldValue
});
布尔值会在订阅时直接触发订阅动作。注意,可以通过args参数获取属性的NewValue和OldValue,不再需要魔法字符串。
替换旧的方式,对每个添加到可观察集合中的物品执行操作:
public void SubscribeToCollection(ObservableCollection users)
{
users.CollectionChanged += users_CollectionChanged;
foreach (var user in users)
{
NewUser(user);
}
}
void users_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
foreach (User user in e.NewItems)
{
NewUser(user);
}
}
private void NewUser(User user)
{
// Do something
}
转化为响应式方式:
users
.ForeachItem()
.Subscribe(user =>
{
// Do something
});
基本上,是将前面两种旧方法的意大利面条代码结合起来。
public void SubscribeToCollection(ObservableCollection users)
{
users.CollectionChanged += users_CollectionChanged;
foreach (var user in users)
{
NewUser(user);
}
}
void users_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
foreach (User user in e.NewItems)
{
NewUser(user);
}
if (e.OldItems != null)
{
foreach (User user in e.OldItems)
{
user.PropertyChanged -= UserChanged;
}
}
}
private void NewUser(User user)
{
user.PropertyChanged += UserChanged;
}
void UserChanged(object sender, PropertyChangedEventArgs e)
{
// Do wonderful stuff when one user changes
}
转化为干净的版本:
users
.ObserveEachItem(u => u.ItemPropertyChanged())
.Subscribe(args =>
{
User user = args.Sender;
// Do wonderful stuff when one user changes
});
是否尝试过以弱引用的方式订阅一个对象,以便垃圾回收器能够正确地回收不再引用的监听器?
一种解决方案是阅读MSDN页面并使用WeakEventManager。抱歉,懒得为阅读。另一种解决方案是使用ObserveWeakly方法:
var subscription = user.ItemPropertyChanged()
.ObserveWeakly()
.Subscribe(args =>
{
User u = args.Sender;
// User changed do what you want...
});
subscription = null;
// OMG forgot to Dispose the subscription !!! memory leak !1!
GC.Collect();
user.AssertSubscribers(0);
// Just kidding
订阅依赖属性的代码并不简单,如下代码片段所示:
TextBox box = new TextBox();
DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(TextBox.BackgroundProperty, typeof(TextBox));
if (dpd != null)
{
dpd.AddValueChanged(box, (sender, args) =>
{
// Do wonderful stuff when the background changed
});
}
但不用担心,现在可以这样做:
TextBox box = new TextBox();
box.DependencyPropertyChanged<Brush>(TextBox.BackgroundProperty)
.Subscribe(args =>
{
Brush brush = args.NewValue;
});
相信不止一次遇到过这样的情况,例如想要一个ObservableCollection<String>,它动态地列出ObservableCollection<User>中所有女孩的名字。
ObservableCollection<String> GirlsNames = new ObservableCollection<String>();
public void SubscribeToCollection(ObservableCollection<User> users)
{
users.CollectionChanged += users_CollectionChanged;
foreach (var user in users)
{
NewUser(user);
}
}
void users_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
foreach (User user in e.NewItems)
{
NewUser(user);
}
if (e.OldItems != null)
{
foreach (User user in e.OldItems)
{
GirlsNames.Remove(user.Name);
}
}
}
private void NewUser(User user)
{
if (user.Gender = Gender.Girl)
GirlsNames.Add(user.Name);
}
users.MapToCollection(GirlsNames, u => u.Name, u => u.Gender == Gender.Girl);