【问题标题】:Using string constant for notify property changed使用字符串常量通知属性已更改
【发布时间】:2014-08-22 12:55:02
【问题描述】:

我正在使用一些现有代码并试图找出在实现 INotifyPropertyChanged 接口时使用字符串常量作为属性名称的优势(如果有的话)。

例如这样做:

/*
 * Why use this instead of string literal
 * in OnPropertyChanged below??
 */
public const string CustomerIdPropertyName = "CustomerId";

private int _customerId;
public int CustomerId
{
    get
    {
         return _customerId;
    }
    set
    {
         if (_cusomterId != value)
         {
              _customerId = value;
              OnPropertyChanged(CustomerIdPropertyName);
         }
    }
}

而不是这个:

private int _customerId;
public int CustomerId
{
    get
    {
         return _customerId;
    }
    set
    {
         if (_cusomterId != value)
         {
              _customerId = value;
              OnPropertyChanged("CustomerId");
         }
    }
}

【问题讨论】:

  • 没有理由使用常量。在功能上它们是相同的。
  • 您的 IDE 可以帮助您输入 CustomerIdPropertyName,这是我认为的唯一优势。
  • @user2348184 好吧,他们是否从其他任何地方引用了该名称?也许是switch(e.PropertyName) {...} 或类似的?

标签: c# wpf inotifypropertychanged


【解决方案1】:

两个版本同样容易出现打字错误。

如果您的 .NET 版本较新,您的属性更改处理程序应如下所示:

protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
  var handler = this.PropertyChanged;
  if (handler != null)
  {
    handler(this, new PropertyChangedEventArgs(propertyName));
  }
}

那么你的属性看起来像这样:

private int _customerId;
public int CustomerId
{
    get
    {
         return _customerId;
    }
    set
    {
         if (_cusomterId != value)
         {
              _customerId = value;
              this.OnPropertyChanged();
         }
    }
}

而且您不会遇到任何打字错误的问题。

【讨论】:

  • 如果属性没有 setter 但在 getter 中计算,那将如何工作,例如通过使用来自外部模型的值?
  • @johanneslink 取决于外部模型。您需要找到一种方法来在外部数据更改时收到通知,然后自己致电this.OnPropertyChanged("YourPropertyName");。
  • @nvoigt 但是我将无法使用 CallerMemberName,因为在调用 OnPropertyChanged() 时我不会在属性的设置器中。我的观点:在我看来,CallerMemberName 仅在属性与 setter 一起使用时才有效。在所有其他情况下,原始问题 - 正如我所读到的那样 - 仍未得到解答:“我们应该将属性名称提取到常量中吗?”
  • @johanneslink 因为这是一个非常特殊的情况,我不得不使用它的一两次,普通的字符串工作得很好。这是“它取决于”的情况:) 如果您想从其他代码中挂钩更改处理程序,一个常量可能很好,如果它只是 WPF/GUI,那么硬编码字符串就足够了。
【解决方案2】:

编译器没有优势,因为两者最终都会是一个常量值。

我无法想象以这种方式使用代码的真正优势。无论哪种方式,都很容易打错字,而且你不会将那个常量重用于任何东西,所以它是没有意义的。

我很高兴看到新的 nameof 关键字在 .NET 的下一个版本中实现。或者更好,如果可能的话,按照 Marc Gravell 的建议使用 [CallerMemberName]。

当拥有没有自己的 getter/setter 的自定义计算属性(例如在 WPF 中)时,nameof 的使用将非常有用。

【讨论】:

  • [CallerMemberName] 在这种情况下工作得很好;不需要nameof
  • 这确实可以从内部工作,而不是外部。
  • 是的,但是大部分 将要监听此事件的代码将是 UI 元素之类的东西 - 他们不会关心实际名称,只需执行TypeConverter.GetProperties(...)[e.PropertyName] 或类似的操作
  • @MarcGravell:这仅在从属性 (-setter) 中调用 OnPropertyChanged 时有效。对于计算属性,通常情况下不存储任何值,也不存在 setter,但它们何时更改是已知的,因此属性更改事件是从其他地方触发的。
  • @O.R.Mapper 真的,真的
【解决方案3】:

回答您的问题(试图找出优势):了解您的类型并等待特定属性更改的观察者有优势

void Observe(Customer c)
{
    c.PropertyChanged += (s, e) => 
    {
        if (e.PropertyName == Customer.CustomerIdPropertyName)
        {
            MessageBox.Show("New id " + Customer.CustomerId);
        }
    }
}

如果你想走得更远:

使用属性选择器表达式填充您的 CustomerIdPropertyName 可以避免输入错误。

nameof 关键字 (CTP) 不需要它。如果你没有这种观察者,CalleMemberNameAttribute 是最简单的方法。

【讨论】:

    【解决方案4】:

    我想这只是为了避免由拼写错误引起的错误,并尝试使代码更易于阅读。此外,如果您更改属性的名称,这意味着更改 const 的值将适用于所有检查属性是否已更改的代码。例如想象一下这段代码:

    public void Main()
    {
        var obj = new ClassWithNotifier();
        obj.OnPropertyChanged += ObjectPropertyChanged;
        DoSomethingWithObj(obj);
    }
    
    private void ObjectPropertyChanged(string propertyName)
    {
        switch (propertyName) {
            case ClassWithNotifier.CustomerIdPropertyName:
                // If the constant changes this will still work
                break;
            case "SomeOtherPropertyName":
                // If you change the property string that is passed here from 
                // your class ClassWithNotifier then this will now break
                break;
        }
    }
    

    在上面的示例中,无论常量的值如何,代码都将起作用,如果您想在某个时候更改属性名称,那么您只需更改常量值,一切仍然可以正常工作而无需在我们检查名称的任何地方找到(显然,如果您想更改常量变量的名称,那么您仍然需要找到这些引用,但是找到对公共字段的引用比在整个项目中搜索魔术字符串更容易)

    【讨论】:

      猜你喜欢
      • 2011-06-25
      • 1970-01-01
      • 1970-01-01
      • 2017-08-20
      • 1970-01-01
      • 2010-11-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多