INotifyPropertyChanged执行的演变:从表达式树到调用方信息的BindableBase类型

INotifyPropertyChanged接口,任何WPF、Silverlight甚至是未来WinRT程序员都必须知道的类型。执行上不用多说,创建一个PropertyChangedEventArgs,数据是属性的名称。随着MVVM模式的大范围应用,让人们不得重新审视INotifyPropertyChanged的执行方式(因为MVVM中的ViewModel通常是执行INotifyPropertyChanged的),显然,最简单的执行方式很不雅观,如下:

string companyName;
public string CompanyName
{
get { return companyName; }
set
{
if (value != companyName)
{
companyName = value;
//调用PropertyChanged事件
OnPropertyChanged(“CompanyName”);
}
}
}

于是.NET 3.5后,表达式树(Expression Tree)出现带来了另一种方式,不管你怎样看待这种方法,但至少开发者不需要自己定义一个字符串代表属性名称,此项工作交给了Expression Tree。

这个改进的方法也是比较流行的,比如微软的Prism框架中的NotificationObject就是这样做的,Prism中的PropertySupport.ExtractPropertyName方法可以提取表达式树中的MemberExpression,并返回PropertyInfo的Name属性,这样开发者只需要定义一个返回指定属性的Expression Tree(通过Lambda表达式)。

还有一些工程中有ViewModelEx的类型和上述功能也是类似的。

比如:

//继承Prism中的NotificationObject,后者执行INotifyPropertyChanged
class MyViewModel : NotificationObject
{
private bool _TaskFinished;
public bool TaskFinished
{
get { return _TaskFinished; }
set
{
if (value != _TaskFinished)
{
_TaskFinished = value;
//通过定义Expression Tree,最终提取属性名称
RaisePropertyChanged(() => TaskFinished);
}
}
}
}

终于到了.NET 4.5,微软对此进行了直接的改进(要知道Expression Tree的直接意图并不是直接针对此的),.NET 4.5中加入了调用者信息,具体是如下三个特性类型:

CallerMemberName, CallerFilePath, CallerLineNumber。全部在System.Runtime.CompilerServices命名空间内。

分别对应调用方成员名称,源代码文件路径和源代码行。

注意不要把调用信息想象成什么很神奇的东西,实际上仅仅是编译中把相应信息作为常量传入到参数中,因此调用信息只能用在具有默认值的方法参数上。

代码:

//+ using System.Runtime.CompilerServices;
class Program
{
static void Main(string[] args)
{
doo();
}

static void doo([CallerMemberName]string member = null,
    [CallerFilePath] string path = null,
    [CallerLineNumber] int line = 0)
{
    Console.WriteLine("调用我的是:文件:{0},成员名称:{1},行数:{2}"
        , path, member, line);
}

}

输出:

调用我的是:文件:c:\Users\Mgen\Documents\Visual Studio 2012\Projects\Mgen\Mgen
Program.cs,成员名称:Main,行数:15

CallerMemberName很智能,当在属性中调用时,返回的是属性名称而不是属性包装的背后方法,否则CallerMemberName就没用了呵呵。

那么我们可以这样定义OnPropertyChanged方法,使用CallerMemberName特性:

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}

接着在属性的set中直接调用,因为有了CallerMemberName特性,根本不需要任何参数,编译器会传入相应的参数,如下:

private int myVar;
public int MyProperty
{
get { return myVar; }
set
{
if (myVar != value)
{
myVar = value;
OnPropertyChanged();
}
}
}

这还没有完,微软既然决定好好改进一下,就不会这样就完了,Visual Studio 2012的Metro应用程序工程中的Common文件夹中有一个BindableBase类型,必须先继承INotifyPropertyChanged:

public abstract class BindableBase : INotifyPropertyChanged

他不仅有上面那个OnPropertyChanged方法,还有一个SetProperty方法:

protected bool SetProperty(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (object.Equals(storage, value)) return false;

storage = value;
this.OnPropertyChanged(propertyName);
return true;

}

就是set中常见的判断值的逻辑,那么有了BindableBase,最后的INotifyPropertyChanged执行就是这样:

class Mgen : BindableBase
{
private int myVar;
public int MyProperty
{
get { return myVar; }
set { SetProperty(ref myVar, value); }
}
}
怎么样?碉堡了,我认为。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值