【问题标题】:Shorter code to trigger property changed events触发属性更改事件的更短代码
【发布时间】:2015-11-10 03:00:17
【问题描述】:

我有一个包含数十个属性的类需要引发属性更改事件,目前我的代码看起来像

public class Ethernet : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string timeStamp;

    public string TimeStamp
    {
        get { return timeStamp; }
        set
        {
            timeStamp = value;

            if(PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("TimeStamp"));
        }
    }
}

在 C# 中是否有更短的方法来编写这种代码,我对每个属性都进行了过多的复制/粘贴操作,我觉得必须有更好的方法。

【问题讨论】:

标签: c# wpf


【解决方案1】:

引用的代码在编写时不是线程安全的。请参阅Pattern for implementing INotifyPropertyChanged? 为什么下面的代码更好,以及在接受的回复中指向 Eric Lippert 博客的链接,为什么故事没有就此结束。

    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null) handler(this, new PropertyChangedEventArgs("TimeStamp"));

有关实际问题的答案,请参阅Implementing INotifyPropertyChanged - does a better way exist?,包括此 C# 6.0 快捷方式。

    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TimeStamp"));

【讨论】:

  • .? 返回null 时,您将拥有类似null; 的代码行。这是不可能的,因此您最终会出现编译时错误。 (但赞成其他信息。)
  • @M.kazemAkhgary - 不,新的 c# 6.0 .? 是一个空检查代码。只有当.? 左侧的值不是null 时,它才会调用Invoke。它编译(在 c# 6.0 中)就好了。
  • @M.kazem 我的理解是 .? null 没有调用也没有错误。除了在链接的 stackoverflow 答案中提到之外,这也是 Mark Michaelis 和 Eric Lippert 用 Essential C# 6.0 编写它的推荐方式(“高级主题:利用委托的空条件运算符”)。
  • 如果您使用 C# 6,最好使用 nameof(TimeStamp) 而不是 TimeStamp。如果您稍后重命名该属性,如果不更改,它将无法编译。
  • @Snicker 这确实是个好建议。但由于我没有发布完整的代码,所以我选择让 sn-ps 尽可能接近原始代码。
【解决方案2】:

看看这个答案:https://stackoverflow.com/a/2339904/259769

我在提供和扩展方法中的代码替换了大部分设置代码,让您将代码缩短为:

public class Ethernet : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string timeStamp;

    public string TimeStamp
    {
        get { return timeStamp; }
        set { this.NotifySetProperty(ref timeStamp, value, () => this.TimeStamp); }
    }
}

此代码的另一个明显优势是它会立即针对属性名称进行强类型化。

【讨论】:

    【解决方案3】:

    在MVVM模式中,经常使用properties changed/ing,一个典型的基本解决方案如下:

      public class ViewModelBase : INotifyPropertyChanged
      {
         public event PropertyChangedEventHandler PropertyChanged;
    
         protected void FirePropertyChanged([CallerMemberName] string propertyName = null)
         {
            if (propertyName == null)
               throw new ArgumentNullException("propertyName");
            try
            {
               this.OnPropertyChanged(propertyName);
            }
            catch (Exception exception)
            {
               Trace.TraceError("{0}.OnPropertyChanged threw {1}: {2}", this.GetType().FullName, exception.GetType().FullName, exception);
            }
         }
         protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
         {
            var handler = PropertyChanged;
            if (handler != null)
            {
               handler(this, new PropertyChangedEventArgs(propertyName));
            }
         }
      }
    
     public class Ethernet : ViewModelBase
      {
         private DataTime timeStamp;
    
         public DateTime TimeStamp
         {
            get
            {
               return timeStamp;
            }
            set
            {
               timeStamp = value;
               FirePropertyChanged();
            }
         }
      }
    

    【讨论】:

      【解决方案4】:

      我爱上了这门课:

      [Serializable]
      public class PropertyChangedBase : INotifyPropertyChanged
      {
          public event PropertyChangedEventHandler PropertyChanged;
      
          protected virtual bool SetProperty<T>(T value, ref T field, Expression<Func<object>> property)
          {
              return SetProperty(value, ref field, GetPropertyName(property));
          }
      
          protected virtual bool SetProperty<T>(T value, ref T field, [CallerMemberName] string propertyName = null)
          {
              if (field == null || !field.Equals(value))
              {
                  field = value;
                  OnPropertyChanged(propertyName);
                  return true;
              }
              return false;
          }
      
          public void OnPropertyChanged([CallerMemberName] string propertyName = null)
          {
              PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
          }
      
          public void OnPropertyChanged(Expression<Func<object>> property)
          {
              OnPropertyChanged(GetPropertyName(property));
          }
      
          protected string GetPropertyName(Expression<Func<object>> property)
          {
              var lambda = property as LambdaExpression;
              MemberExpression memberExpression;
      
              var unaryExpression = lambda.Body as UnaryExpression;
              if (unaryExpression != null)
              {
                  memberExpression = unaryExpression.Operand as MemberExpression;
              }
              else
              {
                  memberExpression = (MemberExpression) lambda.Body;
              }
      
              var propertyInfo = memberExpression?.Member as PropertyInfo;
              return propertyInfo?.Name ?? string.Empty;
          }
      }
      

      这样做的一个巨大优势是它会检查值是否发生了变化。这将最小化对视图的更新调用。对于您的示例,它可能如下所示:

      public class Ethernet : PropertyChangedBase
      {
          private string _timeStamp;
      
          public string TimeStamp
          {
              get { return _timeStamp; }
              set { SetProperty(value, ref _timeStamp); }
          }
      }
      

      如果你想要它真的很舒服,你可以为此编写一个代码 sn-p。这将是 sn-p 部分:

          <Snippet>
            <Declarations>
              <Literal>
                <ID>PropertyName</ID>
                <Type>String</Type>
                <ToolTip>The property name</ToolTip>
                <Default>NewProperty</Default>
              </Literal>
              <Literal>
                <ID>PropertyType</ID>
                <Type>
                </Type>
                <ToolTip>Replace with the type of the property</ToolTip>
                <Default>string</Default>
              </Literal>
              <Object>
                <ID>PrivateVariable</ID>
                <Type>Object</Type>
                <ToolTip>The name of the private variable</ToolTip>
                <Default>newPropertyValue</Default>
              </Object>
            </Declarations>
            <Code Language="csharp" Kind="method decl"><![CDATA[        private $PropertyType$ _$PrivateVariable$;
              public $PropertyType$ $PropertyName$
              {
                  get { return _$PrivateVariable$; }
                  set
                  {
                      SetProperty(value, ref _$PrivateVariable$);
                  }
              }]]></Code>
          </Snippet>
      

      【讨论】:

        猜你喜欢
        • 2011-06-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多