如何在C#6.0中实现INotifyPropertyChanged?
对的回答经过编辑后表明,在C#6.0中,INotifyPropertyChanged可以通过以下OnPropertyChanged过程实现:如何在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)); } 然而,从这个答案中还不清楚相应
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); } }
}