添加了 C# 6 答案
在 C# 6(以及 Visual Studio 2015 附带的任何版本的 VB)中,我们有nameof
运算符,使事情变得比以往更容易。在下面的原始答案中,我使用 C# 5 功能(来电者信息属性)来处理“自我更改”通知的常见情况。这nameof
运算符可以在所有情况下使用,并且在“相关属性更改”通知场景中特别有用。
为简单起见,我想我将保留常见的自我更改通知的呼叫者信息属性方法。更少的打字意味着更少的打字错误和复制/粘贴引起的错误的机会......这里的编译器确保您选择有效的类型/成员/变量,但它不能确保您选择正确的类型/成员/变量。然后使用新的就很简单了nameof
相关属性更改通知的运算符。下面的示例演示了调用者信息属性的关键行为...如果参数由调用者指定,则该属性对参数没有影响(即,仅当参数被省略时,才为参数值提供调用者信息)呼叫者,召集者)。
还值得观察的是nameof
PropertyChanged 事件处理程序也可以使用运算符。现在您可以比较PropertyName
事件中的值(这是string
)使用特定属性nameof
运算符,消除更多魔术字符串。
参考信息nameof
here: https://msdn.microsoft.com/en-us/library/dn986596.aspx https://msdn.microsoft.com/en-us/library/dn986596.aspx
Example:
public class Program
{
void Main()
{
var dm = new DataModel();
dm.PropertyChanged += propertyChangedHandler;
}
void propertyChangedHandler(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == nameof(DataModel.NumberSquared))
{
//do something spectacular
}
}
}
public class DataModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
public class DataModel : DataModelBase
{
//a simple property
string _something;
public string Something
{
get { return _something; }
set { _something = value; OnPropertyChanged(); }
}
//a property with another related property
int _number;
public int Number
{
get { return _number; }
set
{
_number = value;
OnPropertyChanged();
OnPropertyChanged(nameof(this.NumberSquared));
}
}
//a related property
public int NumberSquared { get { return Number * Number; } }
}
原始 C# 5 答案
从 C# 5 开始,最好使用调用者信息属性,这在编译时解决,无需反射。
我在基类中实现这个,派生类只需调用OnPropertyChanged
方法来自其属性设置器。如果某个属性隐式更改了另一个值,我也可以在属性设置器中使用该方法的“显式”版本,这样就不再“安全”,但这是我接受的罕见情况。
或者,您可以使用此方法进行自我更改通知,并使用 @Jehof 给出的答案进行相关属性更改通知...这将具有没有魔术字符串的优点,并且对于自我更改通知的常见情况具有最快的执行速度。
这个最新的建议在下面实现(我想我会开始使用它!)
public class DataModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
OnPropertyChangedExplicit(propertyName);
}
protected void OnPropertyChanged<TProperty>(Expression<Func<TProperty>> projection)
{
var memberExpression = (MemberExpression)projection.Body;
OnPropertyChangedExplicit(memberExpression.Member.Name);
}
void OnPropertyChangedExplicit(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
public class DataModel : DataModelBase
{
//a simple property
string _something;
public string Something
{
get { return _something; }
set { _something = value; OnPropertyChanged(); }
}
//a property with another related property
int _number;
public int Number
{
get { return _number; }
set
{
_number = value;
OnPropertyChanged();
OnPropertyChanged(() => NumberSquared);
}
}
//a related property
public int NumberSquared { get { return Number * Number; } }
}