C# 触发属性更改事件的较短代码

C# 触发属性更改事件的较短代码,c#,wpf,C#,Wpf,我有一个拥有数十个属性的类,需要引发属性更改事件,目前我的代码看起来像 public class Ethernet : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private string timeStamp; public string TimeStamp { get { return timeStamp; }

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

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语言中有没有一种更短的方法来编写这种代码,我对每个属性都做了过多的复制/粘贴操作,我觉得一定有更好的方法

引用的代码不是线程安全的。看看为什么下面的代码更好,并在接受的回复中链接到Eric Lippert的博客为什么故事没有结束

    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null) handler(this, new PropertyChangedEventArgs("TimeStamp"));
有关实际问题的答案,请参见包含此C#6.0快捷方式

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

一定要看看这个答案:

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

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); }
    }
}

此代码的另一个显著优点是,它会立即根据属性名称进行强类型化。

在MVVM模式中,经常使用更改的属性,典型的基本解决方案如下所示:

  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();
        }
     }
  }

我爱上了这个班:

[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;
    }
}
如果您希望它非常舒适,您可以为此编写一个代码段。这将是代码段部分:

    <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>

属性名
一串
属性名
新财产
属性类型
替换为属性的类型
一串
私有变量
对象
私有变量的名称
新属性值


不太可能,但您可以使用Fody(免费)out PostSharp(付费)等工具来简化操作。当
返回null时,可能会出现重复。
返回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)
而不是
时间戳
。如果以后重新命名该属性,如果不进行更改,它将无法编译。@Snicker这确实是一个很好的建议。但是因为我没有发布完整的代码,所以我选择尽可能地保持代码片段与原始代码的接近。
    <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>