Class.PropertyName 上的 Switch-Case(不是值)
Switch-Case on Class.PropertyName (not value)
我有一个 class 实现 INotifyPropertyChanged
是这样的:
public class Person : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
string _color;
public string Color
{
get{ return _color; }
set
{
_color = value;
RaisePropertyChanged();
}
}
...
private void RaisePropertyChanged([CallerMemberName]string prop = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
当调用 Color
属性 setter 时,它调用 RaisePropertyChanged()
自动获取 属性 名称,即 "Color"
并使用填充 PropertyChangedEventArgs
。而不是您手动输入 属性 名称。
这很好,因为它可以防止代码中可能出现的错误,因为您不必手动键入 属性 名称。在重构代码时也有帮助,因为您没有对任何字符串进行硬编码。
我的问题
我有一个 PropertyChanged
的事件处理程序。如何在不将 属性 名称硬编码为字符串的情况下使用 switch-case 构造。所以像这样:
void Person_PropertyChanged(object sender, PropertyChangedEventArgs e){
switch (e.PropertyName)
{
case PropertyNameOf(Person.Color);
//some stuff
break;
default:
break;
}
}
这可能吗?我想这样做,这样我就可以保留上面提到的好处。
你可以使用Expression<Func<T>>
做你想做的事。
定义此方法:
private string ToPropertyName<T>(Expression<Func<T>> @this)
{
var @return = string.Empty;
if (@this != null)
{
var memberExpression = @this.Body as MemberExpression;
if (memberExpression != null)
{
@return = memberExpression.Member.Name;
}
}
return @return;
}
那你可以这么写:
void Person_PropertyChanged(object sender, PropertyChangedEventArgs e){
switch (e.PropertyName)
{
case ToPropertyName(() => Person.Color);
//some stuff
break;
default:
break;
}
}
现在你有了一些强类型的快乐。 :-)
要在没有 switch
和凌乱的 if
/then
/else
的情况下获得类似开关的功能,您可以这样做:
void Person_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var @switch = new Dictionary<string, Action>()
{
{ ToPropertyName(() => Person.Color), () => { /* some stuff */ } },
{ ToPropertyName(() => Person.Size), () => { /* some other stuff */ } },
{ ToPropertyName(() => Person.Shape), () => { /* some more stuff */ } },
};
if (@switch.ContainsKey(e.PropertyName))
{
@switch[e.PropertyName]();
}
else
{
/* default stuff */
}
}
在 C# 6.0 中,您可以使用 nameof() 关键字。
关键字在编译时被字符串文字替换。因此,从性能的角度来看,它比使用 lambda 表达式和在运行时挖掘符号名称的代码要好得多,而且它还适用于 switch()
语句:
switch(e.PropertyName)
{
case nameof(Foo.Bar):
break;
}
如果您在 class 中更改 属性 的名称,但忘记在 switch 语句中更改它,您也会遇到编译时错误。所以这种方法更不容易出错。
我有一个 class 实现 INotifyPropertyChanged
是这样的:
public class Person : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
string _color;
public string Color
{
get{ return _color; }
set
{
_color = value;
RaisePropertyChanged();
}
}
...
private void RaisePropertyChanged([CallerMemberName]string prop = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
当调用 Color
属性 setter 时,它调用 RaisePropertyChanged()
自动获取 属性 名称,即 "Color"
并使用填充 PropertyChangedEventArgs
。而不是您手动输入 属性 名称。
这很好,因为它可以防止代码中可能出现的错误,因为您不必手动键入 属性 名称。在重构代码时也有帮助,因为您没有对任何字符串进行硬编码。
我的问题
我有一个 PropertyChanged
的事件处理程序。如何在不将 属性 名称硬编码为字符串的情况下使用 switch-case 构造。所以像这样:
void Person_PropertyChanged(object sender, PropertyChangedEventArgs e){
switch (e.PropertyName)
{
case PropertyNameOf(Person.Color);
//some stuff
break;
default:
break;
}
}
这可能吗?我想这样做,这样我就可以保留上面提到的好处。
你可以使用Expression<Func<T>>
做你想做的事。
定义此方法:
private string ToPropertyName<T>(Expression<Func<T>> @this)
{
var @return = string.Empty;
if (@this != null)
{
var memberExpression = @this.Body as MemberExpression;
if (memberExpression != null)
{
@return = memberExpression.Member.Name;
}
}
return @return;
}
那你可以这么写:
void Person_PropertyChanged(object sender, PropertyChangedEventArgs e){
switch (e.PropertyName)
{
case ToPropertyName(() => Person.Color);
//some stuff
break;
default:
break;
}
}
现在你有了一些强类型的快乐。 :-)
要在没有 switch
和凌乱的 if
/then
/else
的情况下获得类似开关的功能,您可以这样做:
void Person_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var @switch = new Dictionary<string, Action>()
{
{ ToPropertyName(() => Person.Color), () => { /* some stuff */ } },
{ ToPropertyName(() => Person.Size), () => { /* some other stuff */ } },
{ ToPropertyName(() => Person.Shape), () => { /* some more stuff */ } },
};
if (@switch.ContainsKey(e.PropertyName))
{
@switch[e.PropertyName]();
}
else
{
/* default stuff */
}
}
在 C# 6.0 中,您可以使用 nameof() 关键字。
关键字在编译时被字符串文字替换。因此,从性能的角度来看,它比使用 lambda 表达式和在运行时挖掘符号名称的代码要好得多,而且它还适用于 switch()
语句:
switch(e.PropertyName)
{
case nameof(Foo.Bar):
break;
}
如果您在 class 中更改 属性 的名称,但忘记在 switch 语句中更改它,您也会遇到编译时错误。所以这种方法更不容易出错。