目录
所以现在的重点是EarlySourceBindingExpression是做什么的?
项目URL和NuGet
代码
让我们从结果开始。这些测试通过:
[Fact]
public void ShouldNotifyCorrectlyIfNestedPropertyChange()
{
var house = new House();
int counter = 0;
house
.WhenChanged(h => h.MainRoom.Table)
.WhenChanged(h => h.Name)
.With<PropertyChangeObserver>()
.Do(() => counter++);
house.MainRoom = new();
house.MainRoom.Table = new();
house.Name = "NewName";
counter.Should().Be(3);
}
[Fact]
public void ShouldNotNotifyIfPropertyChangesOnOldReference()
{
var house = new House
{
MainRoom = new()
{
Table = new()
}
};
var counter = 0;
house
.WhenChanged(h => h.MainRoom.Table)
.With<PropertyChangeObserver>()
.Do(() => counter++);
var oldRoom = house.MainRoom;
house.MainRoom = new();
oldRoom.Table = new();
counter.Should().Be(1);
}
它是如何工作的?
让我们从头开始。WhenChanged是这样声明的:
public static IExpressionChangedBuilder<TSource> WhenChanged<TSource, TValue>
(this TSource @this, Expression<Func<TSource, TValue>> expression)
{
IExpressionChangedBuilder<TSource> ret =
new EarlySourceBindingExpression<TSource>(@this);
return ret.WhenChanged(expression);
}
所以现在的重点是EarlySourceBindingExpression是做什么的?
您可以看到,此类需要为构建(@this)一个参数。这将是从中开始“搜索”要绑定到的属性的对象。
扩展方法的第二个参数是表达式。
你可以把它看作是lambda:h => h.MainRoom.Table,不被视为一个函数,而是作为组成lambda的所有“部分”的集合(树)。所以把它想成{h, h.MainRoom, h.MainRoom.Table}。表达式的每个部分(即 {h, h.MainRoom, h.MainRoom.Table}中的每个元素)都是一个对象,其中包含完全分类自身所需的所有元数据。因此,如果我们放置一个断点,探索表达式,我们可以看到:
- h是ParameterExpression实例
- h.MainRoom是MemberExpression(Member = Property)实例
- h.MainRoom.Table就是一个MemberExpression实例。
我们需要了解的是,MemberExpression类有一个叫做Member的字段。后者包含有关它所代表的成员的所有信息(最右边的一个),在这些之间,还有Name。在我们的例子中,因此我们可以分别从h.MainRoom和h.MainRoom.Table获得string “MainRoom”和“Table”。
现在我们有了属性名称?
现在我们有了string “MainRoom”和“Table”,我们只需要分别订阅h和h.MainRoom的NotifyPropertyChanged事件,并监听名为“MainRoom”或“Table”的属性的变化。如果是这种情况,我们只需调用回调。
如果h.MainRoom是新的怎么办?
如果h.MainRoom更改,这意味着我们必须取消订阅所有旧的嵌套属性(旧的h.MainRoom),并重新订阅所有新的。这是自动完成的。
如果我需要完全取消订阅所有人怎么办?
当您订阅时,您已经返回了一个IDisposable,如果调用会自动为您取消订阅所有内容,从而释放所有事件。
[Fact]
public void ShouldNotNotifyIfDisposed()
{
var house = new House
{
MainRoom = new Room
{
Table = new()
}
};
var counter = 0;
var subscription = house
.WhenChanged(h => h.MainRoom.Table)
.With<propertychangeobserver>()
.Do(() => counter++);
subscription.Dispose();
house.MainRoom = new();
counter.Should().Be(0);
}
https://www.codeproject.com/Tips/5338061/Fluent-Expression-Tree-Binding-in-MVVM