表达式树在数据绑定中的应用

C#3.0中引入的诸多语言特性中,表达式树是其中之一。它允许请求编译器生成代表代码行的数据,而不是仅仅准备代码以供执行。这使得反射的好处与编译器检查的代码的好处得以结合。例如,不再需要使用字符串字面量来元识别属性;相反,可以使用像 o => o.SomeProperty 这样的 lambda 表达式。

2013年1月,花了大量时间将表达式树应用于一个远不止简单属性示例的领域。结果不仅展示了代码作为数据的强大之处,还展示了数据绑定概念的适用性,它不仅限于用户界面,而是可以广泛应用于编码。

这个项目源于需要管理视图模型和模型类上的许多相互依赖的属性。如果逐个处理,将导致大量的事件处理程序。坚定不移的目标是使应用程序代码尽可能简洁和声明式,这促使寻找替代方案。有什么比在一个地方简单地声明 X = Y,然后让其余的工作透明地为处理更可取的呢?当然,没有。所以创建了以下接口,并实现了使其工作的内部逻辑:

public interface IPropertiesUpdater<TTarget> { PropertiesUpdater<TTarget> AddProperty<TProperty>( Expression<Func<TTarget, TProperty>> targetPropertyExp, Expression<Func<TProperty>> valueExp, bool setInitialValue = false ); }

将展示一些使用示例,以展示 PropertiesUpdater 提供的优势。目前正在开发的餐厅POS系统包括一个 Table 域对象,它代表坐下的桌子类型。Table 的 OpenOrder 属性在用户界面中绑定,因此其私有 setter 会引发属性更改事件以通知 UI 更改。Table 的构造函数包含以下代码,用于确定 OpenOrder 的值:

new PropertiesUpdater<Table>(this) .AddProperty( t => t.OpenOrder, () => DineInParts.Select(p => p.Owner).SingleOrDefault(o => o != null && o.IsOpen) );

PropertiesUpdater 识别大多数 LINQ-to-Objects 方法,并可以解释传递给它们 lambda 表达式。在上述示例中调用 AddProperty 后,以下每种情况都会导致目标 Table.OpenOrder 属性更新:

  • Table.DineInParts 集合属性的值发生变化;
  • 集合实例的元素添加/移除;
  • 通过集合元素引用的 Owner 和 IsOpen 属性的值发生变化。

在底层,PropertiesUpdater 依赖于引用对象实现 INotifyPropertyChanged 和引用集合实现 INotifyCollectionChanged。

接下来,将展示两个来自餐厅POS系统订单详情屏幕视图模型构造函数的使用示例:

// 基于域模型中的源来确定电话号码和名称。 // 初始化这些,而不会导致联系人被覆盖。 new PropertiesUpdater<OrderDetailsScreenPM>(this) .AddProperty( pm => pm.PhoneNumber, () => domainItem.Contact is VerifiedCustomer ? ((VerifiedCustomer)domainItem.Contact).PhoneNumber : null, setInitialValue: true ).AddProperty( pm => pm.Name, () => domainItem.Contact != null ? domainItem.Contact.Name : null, setInitialValue: true );

在这个示例中,可能会注意到,允许的“一行”值表达式实际上跨越了多行。“一行”限制只是意味着表达式不能包含分号。实际上,表达式可以是所需的任何复杂度。

在第一个实例化的 PropertiesUpdater 的值表达式中,注意属性路径的存在,这些路径遍历了多个属性。这些是 domainItem.Contact.PhoneNumber 和 domainItem.Contact.Name。如果 Contact 的值发生变化怎么办?以前的联系人的值不再感兴趣(特别是以前的联系人的 PhoneNumber 和 Name 的值),因此 PropertiesUpdater 停止观察以前的联系人。这与从集合中移除项目的情况类似,在这种情况下,移除的元素不再被观察。相反,PropertiesUpdater 将开始观察新引入的值。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485