如何在C#6.0中实现INotifyPropertyChanged?

如何在C#6.0中实现INotifyPropertyChanged?,c#,inotifypropertychanged,c#-5.0,c#-6.0,C#,Inotifypropertychanged,C# 5.0,C# 6.0,对的回答经过编辑后表明,在C#6.0中,INotifyPropertyChanged可以通过以下OnPropertyChanged过程实现: protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } 然而,从这个答案中还不清楚相应

对的回答经过编辑后表明,在C#6.0中,INotifyPropertyChanged可以通过以下OnPropertyChanged过程实现:

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

然而,从这个答案中还不清楚相应的属性定义应该是什么。在C#6.0中,当使用此构造时,INotifyPropertyChanged的完整实现是什么样子的?

合并各种更改后,代码将如下所示。我已经用注释强调了改变的部分,以及每个部分是如何起作用的

public class Data : INotifyPropertyChanged
{ 
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        //C# 6 null-safe operator. No need to check for event listeners
        //If there are no listeners, this will be a noop
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    // C# 5 - CallMemberName means we don't need to pass the property's name
    protected bool SetField<T>(ref T field, T value,
    [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) 
            return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    private string name;
    public string Name
    {
        get { return name; }
        //C# 5 no need to pass the property name anymore
        set { SetField(ref name, value); }
    }
}
公共类数据:INotifyPropertyChanged
{ 
公共事件属性更改事件处理程序属性更改;
受保护的OnPropertyChanged无效([CallerMemberName]字符串propertyName=null)
{
//C#6空安全运算符。无需检查事件侦听器
//如果没有听众,这将是一个noop
PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
}
//C#5-CallMemberName意味着我们不需要传递属性的名称
受保护的布尔设置字段(参考T字段、T值、,
[CallerMemberName]字符串propertyName=null)
{
if(EqualityComparer.Default.Equals(字段,值))
返回false;
字段=值;
OnPropertyChanged(propertyName);
返回true;
}
私有字符串名称;
公共字符串名
{
获取{返回名称;}
//C#5不再需要传递属性名称
set{SetField(ref name,value);}
}
}

我在项目中使用相同的逻辑。我的应用程序中的所有视图模型都有一个基类:

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class PropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
每个视图模型都继承自该类。现在,在每个属性的setter中,我只需要调用
OnPropertyChanged()

它为什么起作用?

[CallerMemberName]
由编译器自动填充调用此函数的成员的名称。当我们从
Initialized
调用
OnPropertyChanged
时,编译器将
nameof(Initialized)
作为
OnPropertyChanged

另一个需要记住的重要细节


该框架要求
PropertyChanged
,并且您绑定到的所有属性都是
public
我知道这个问题很老了,但这里是我的实现

Bindable将字典用作属性存储。为子类添加必要的重载以使用ref参数管理其自己的支持字段非常容易

  • 没有魔术弦
  • 不思考
  • 可以改进以抑制默认字典查找
守则:

    public class Bindable : INotifyPropertyChanged
    {
        private Dictionary<string, object> _properties = new Dictionary<string, object>();

        /// <summary>
        /// Gets the value of a property
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <returns></returns>
        protected T Get<T>([CallerMemberName] string name = null)
        {
            object value = null;
            if (_properties.TryGetValue(name, out value))
                return value == null ? default(T) : (T)value;
            return default(T);
        }

        /// <summary>
        /// Sets the value of a property
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <param name="name"></param>
        protected void Set<T>(T value, [CallerMemberName] string name = null)
        {
            if (Equals(value, Get<T>(name)))
                return;
            _properties[name] = value;
            OnPropertyChanged(name);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
公共类可绑定:INotifyPropertyChanged
{
私有字典_properties=新字典();
/// 
///获取属性的值
        /// 
/// 
/// 
受保护的T Get([CallerMemberName]字符串名称=null)
{
对象值=空;
if(_properties.TryGetValue(名称,输出值))
返回值==null?默认值(T):(T)值;
返回默认值(T);
}
/// 
///设置属性的值
/// 
/// 
/// 
/// 
受保护的无效集(T值,[CallerMemberName]字符串名称=null)
{
if(等于(值,获取(名称)))
返回;
_属性[名称]=值;
OnPropertyChanged(名称);
}
公共事件属性更改事件处理程序属性更改;
受保护的虚拟void OnPropertyChanged([CallerMemberName]字符串propertyName=null)
{
PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
}
}
像这样使用

public class Item : Bindable
{
     public Guid Id { get { return Get<Guid>(); } set { Set<Guid>(value); } }
}
公共类项:可绑定
{
公共Guid Id{get{return get();}set{set(value);}}
}

设置字段的结果值用于什么?如果实际进行了替换并触发了事件,则为真。它不在本节中使用snippet@panagiotis-kanavos:关于你建议我直接与另一个问题的撰稿人沟通的建议,我会更进一步,建议我应该把我最初的问题作为评论贴在答案下面。但是,StackOverflow规则不允许我做这两件事,所以这是我能做的最好的事情。为什么SetField返回true/false?在c#6中,您还可以对getter和setter使用类似lambda的语法:
get=>name;set=>SetField(参考名称、值)这可能是对原始问题的一个好答案,但这里的答案不好。OP询问一些现有代码在多次更新后会如何处理。充其量,这应该是一个评论。此外,你没有抓住原始答案的重点——与其到处传播硬编码的值检查和通知调用,你能用一种通用的方式封装所有这些,允许自定义相等比较吗?我不同意这些批评。我觉得很好。你可以通过比较做一些事情,但是。。。我想我对代码示例强调得太多了,我应该强调“它为什么工作?”。每个人,关注“为什么它能工作?”另一个问题/答案已经包含了所有的信息。。。每个集合将只是
集合{SetField(ref name,value);}
SetField
方法已完整显示。@MarcGravel,是的,但我不清楚C#5和C#6的添加是否是为了增加或取代SetField位,我不能要求对该问题进行澄清,所以我不得不问一个新问题。我很高兴我这么做了,因为看到整个课程都写出来,消除了所有的歧义,使它很容易理解。实际上,这是C#5。这种方法有两个问题。首先要注意值类型的装箱/拆箱,其次它会失去类型安全性。调用者可以调用Set(“myProp”),但获取(“myProp”),这将导致
public class Item : Bindable
{
     public Guid Id { get { return Get<Guid>(); } set { Set<Guid>(value); } }
}