【问题标题】:how to prevent infinite property change如何防止无限的属性更改
【发布时间】:2011-07-14 16:53:17
【问题描述】:

假设我有销售价格、首付金额、首付百分比和贷款金额。当用户更改这些属性中的任何一个时,需要更新其他属性以反映新值。您如何处理这种类型的无限属性更改事件?

【问题讨论】:

  • 我很困惑 - 是什么让这个无限?
  • 在这种情况下,它与首付百分比如何四舍五入有关。否则,您可能会达到值将计算相同值并停止触发属性更改事件的地步。

标签: c# xaml inotifypropertychanged


【解决方案1】:

当需要跨多个属性进行流控制时,我将建立一个流控制变量 - 一个布尔值 - 并且在每个被更改的属性中,我将添加一个测试以查看我是否处于流控制之下。

private bool controlledChange = false;

public property int MyVal1
{
    set 
    {
        _myVal1 = value;
        if(!controlledChange)
        {
            controlledChange = true;
            MyVal2 -= 1;
            controlledChange = false;
        }
    }
}

public property int MyVal2
{
    set 
    {
        _myVal2 = value;
        if(!controlledChange)
        {
            controlledChange = true;
            MyVal1 += 1;
            controlledChange = false;
        }
    }
}

这样,任何属性被更改都可以在其他属性之间发起更改,但是当它们被更改时,它们不会依次发起自己的一组更改。

如果这些属性可以有计算结果,您还应该尽可能多地使这些属性为只读,这样您就可以限制对象的更改方式。

【讨论】:

  • 我喜欢称这些“STFU”布尔值。但是,这种类型的模式确实有一个限制,如果由于某种原因将两段代码设置为 true,则第一个“out”会将其设置回 false。根据我的经验,这种情况通常很少见,但可能会根据情况发生。解决这个问题的时间比我在这里要长。 ;)
  • 您可能会考虑在这种情况下引入互斥锁。
  • 如何在答案上放鞭炮,让每个人都能看到它及其美丽的简洁性?
【解决方案2】:

最简单的方法是仅在属性确实发生更改时引发更改事件:

public decimal SalePrice {
   get{
       return salePrice;
   }
   set {
        if (salePrice != value) {
          salePrice = value; // putting as first statement prevents the setter 
                             // to be entered again ...
          RaiseSalePriceChange();
          // Set other properties
        }
   }
}

【讨论】:

    【解决方案3】:

    我不确定我是否完全理解,因为我不知道你所说的“无限”是什么意思

    这可能是一个用字段实际支持属性的好用例。这样,您可以在属性集上触发事件,但在内部一次设置一个字段,而不会触发 N 个事件。

    class MyClass
    {
        private string m_Name;
        private int m_SomeValue;
    
        public string Name
        {
            get { return m_Name; }
            set
            {
                if (value != m_Name)
                {
                    m_Name = value;
                    m_SomeValue++;
    
                    // Raise Event
                }
            }
        }
    
        public int SomeValue
        {
            get { return m_SomeValue; }
            set
            {
                if (m_SomeValue != value)
                {
                    m_SomeValue = value;
                    // Raise Event
                }
            }
        }
    

    【讨论】:

      【解决方案4】:

      如果确实需要INotifyPropertyChanged 来通知外部对象,那么我将集中所有内容。像这样:

          private double salesPrice;
          private double downPaymentAmount;
          private double downPaymentPercent;
          private double loanAmount;
      
          public double SalesPrice
          {
              get
              {
                  return salesPrice;
              }
              set
              {
                  if (salesPrice != value)
                  {
                      salesPrice = value;
      
                      // maybe you would rather use a RecalculateForSalePriceChanged() method
                      RecalculateDownPaymentAmount();
                      RecalculateDownPaymentPercent();
                      RecalculateLoanAmount();
      
                      propertiesChanged();
                  }
              }
          }
      
          public double DownPaymentAmount
          {
              get
              {
                  return downPaymentAmount;
              }
              set
              {
                  if (downPaymentAmount != value)
                  {
                      downPaymentAmount = value;
      
                      // see above
                      RecalculateDownPaymentPercent();
                      RecalculateLoanAmount();
                      RecalculateSalesPrice();
      
                      propertiesChanged();
                  }
              }
          }
      
          public double DownPaymentPercent
          {
              get
              {
                  return downPaymentPercent;
              }
              set
              {
                  if (downPaymentPercent != value)
                  {
                      downPaymentPercent = value;
      
                      // see above
                      RecalculateDownPaymentAmount();
                      RecalculateLoanAmount();
                      RecalculateSalesPrice();
      
                      propertiesChanged();
                  }
              }
          }
      
          public double LoanAmount
          {
              get
              {
                  return loanAmount;
              }
              set
              {
                  if (loanAmount != value)
                  {
                      loanAmount = value;
      
                      // see above
                      RecalculateDownPaymentAmount();
                      RecalculateDownPaymentPercent();
                      RecalculateSalesPrice();
      
                      propertiesChanged();
                  }
              }
          }
      
          private void propertiesChanged()
          {
              RaisePropertyChanged("SalesPrice", "DownPaymentAmount", "DownPaymentPercent", "LoanAmount");
          }
      

      也许您可以将重新计算集中在更少的方法甚至一个方法中,但我不知道您是如何计算它们的。但当然,在重新计算值时,您必须保持特定的顺序。 由于它们只对字段进行操作,不会更改属性,因此不会有 PropertyChanged-feedback-loop。

      希望这会有所帮助,我没有误解你想要什么。

      【讨论】:

        【解决方案5】:

        OP 想要的就是跟随

        class A : INotifyPropertyChanged
        {
            private int field1;
            public int Property1
            {
                get { return field1; }
                set
                {
                    field1 = value;
                    field2++;
                    RaisePropertyChanged("Property1");
                    RaisePropertyChanged("Property2");
                }
            }
        
            private int field2;
            public int Property2
            {
                get { return field2; }
                set
                {
                    field2 = value;
                    field1++;
                    RaisePropertyChanged("Property1");
                    RaisePropertyChanged("Property2");
                }
            }
        
            public event PropertyChangedEventHandler PropertyChanged;
        
            private void RaisePropertyChanged(string propertyName)
            {
                var handler = PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
        

        他可能正在做的是处理他提到的每个属性的 setter 中的其他属性,从而导致 setter 的循环调用。

        维杰

        【讨论】:

          猜你喜欢
          • 2023-03-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-03-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多