检测RxUI中的循环引用

时间:2016-12-05 21:11:05

标签: reactiveui

我设置了两个相互抵消的复选框。

我认为看到的行为是一个无限循环(复选框1检查2检查1检查2 ...)

相反,在RxUI检查另一个其他复选框后,停止更改传播。

在RxUI中是否有某种循环引用的检测?

using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using System;

namespace rxnested
{
    public class VM01 : ReactiveObject
    {
        [Reactive]
        public bool Prop1 { get; set; }

        [Reactive]
        public bool Prop2 { get; set; }

        public VM01()
        {
            this.WhenAnyValue(x => x.Prop1)
                .Subscribe(x => Prop2 = !x); 


            this.WhenAnyValue(x => x.Prop2)
                .Subscribe(x => Prop1 = !x);
        }
    }
}

<Window x:Class="rxnested.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:rxnested"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="350"
        Width="525">
    <Window.DataContext>
        <local:VM01></local:VM01>
    </Window.DataContext>
    <StackPanel>
        <CheckBox IsChecked="{Binding Prop1}"></CheckBox>
        <CheckBox IsChecked="{Binding Prop2}"></CheckBox>
    </StackPanel>
</Window>

1 个答案:

答案 0 :(得分:2)

我认为您希望代码导致无限循环是错误的。

有两种情况。我们分开考虑一下。

1)假设最初两个属性具有相同的值(假设它是假的)。将P1更改为true将触发第一次订阅,这将尝试将P2设置为当前P1的反向(反向为真== false)。这意味着它会尝试将false设置为已经错误的东西,因此不会发生任何事情(这就是SELECT Db_name(database_id) Database_Name ,o.NAME ,indexname = i.NAME ,i.index_id ,reads = user_seeks + user_scans + user_lookups ,writes = user_updates ,rows = (SELECT Sum(p.rows) FROM sys.partitions p WHERE p.index_id = s.index_id AND s.object_id = p.object_id) ,CASE WHEN s.user_updates < 1 THEN 100 ELSE 1.00 * ( s.user_seeks + s.user_scans + s.user_lookups ) / s.user_updates END AS reads_per_write ,'DROP INDEX ' + Quotename(i.NAME) + ' ON ' + Quotename(c.NAME) + '.' + Quotename(Object_name(s.object_id)) AS 'drop statement' FROM sys.dm_db_index_usage_stats s INNER JOIN sys.indexes i ON i.index_id = s.index_id AND s.object_id = i.object_id INNER JOIN sys.objects o ON s.object_id = o.object_id INNER JOIN sys.schemas c ON o.schema_id = c.schema_id WHERE Objectproperty(s.object_id, 'IsUserTable') = 1 AND s.database_id = Db_id() AND i.type_desc = 'nonclustered' AND i.is_primary_key = 0 AND i.is_unique_constraint = 0 AND (SELECT Sum(p.rows) FROM sys.partitions p WHERE p.index_id = s.index_id AND s.object_id = p.object_id) > 10000 ORDER BY reads_per_write ASC 给你的东西 - 在设置属性之前,它会检查它是否已经改变)。

2)现在P1最初为真,P2为假。将P1更改为false会触发第一个订阅,即将P2设置为(!P1),在这种情况下为true。因此,P2从false变为true。这会触发第二次订阅,尝试将P1设置为(!P2),现在为false。但P1已经是假的,因此属性不会改变(因此NotifyPropertyChanged不会触发)。

因此,通过不盲目设置属性来破坏循环,但首先检查它是否发生了变化(以避免不必要的NotifyPropertyChanged事件)。

当您意识到[Reactive]转化为:

时,这一点就更加清晰了
[Reactive]

注意名称 - RaiseAndSet IfChanged

现在,如果你想看到一个无限循环......只需将你的代码改为:

    private bool _prop2;
    public bool Prop2
    {
        get { return _prop2; }
        set { this.RaiseAndSetIfChanged(ref _prop2, value); }
    }

这给了我 public VM01() { this.WhenAnyValue(x => x.Prop1) .Subscribe(x => Prop2 = !Prop2); this.WhenAnyValue(x => x.Prop2) .Subscribe(x => Prop1 = !Prop1); }