- 属性
程序的本质就是“数据+算法”,在程序中,数据表现为各种各样的变量,算法表现为各种各样的函数。
在面向对象的时代,类把散落在程序中的变量和函数进行归档并控制对它们的访问。被封装在类里的变量称为字段(Field),表示的是类或实例的状态;被封装在类里的函数称为方法,表示的是类或实例的功能。
在类中,我们可以用Private和Public来表示字段的访问级别,如果全部使用public,直接将数据暴露给外界是很不安全的,如果使用private外界则无法访问,增加额外的函数又显得很分散,微软在.Net Frameword中,通过一堆Get/Set方法合并推出了属性的概念。
- 依赖属性
WPF中,微软将属性这个概念推进了一步,提出了“依赖属性”,即一种可以自己没有值,但能通过使用Binding从数据源获得值的属性。
传统的.Net开发中,一个对象所占用的内存空间在调用new进行实例化的时候就已经确定了,而WPF允许对象在被创建的时候并不包含用于存储数据的空间(即字段所占用的空间)、只保留在需要用到数据时能够获得数据的能力,这种能力包括自行获得默认值、借用其他对象数据或实时分配空间。具备这种能力的对象就是依赖对象,而依赖对象获取数据的能力就是依靠依赖属性来实现的。
在WPF系统中,依赖对象被DependencyObject类实现,依赖属性由DependencyProperty类实现。DependencyObject是WPF系统中相当底层的一个基类,所以WPF的所有UI控件都是依赖对象。DependencyProperty必须以DependencyObject为宿主,借助它的SetValue和GetValue进行写入和读取。以Student类为例,Name声明为依赖属性,代码如下:
public class Student:DependencyObject { public string Name { get { return (string)GetValue(NameProperty); } set { SetValue(NameProperty, value); } } public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student)); }
由此可见,自定义依赖属性,宿主必须是DependencyObject的派生类,变量的生命必须使用public static readonly修饰,实例并非使用new而是使用DependencyProperty.Register方法生成。
Register是用宿主的CLR属性名称和宿主的类型的hash code做异或运算得到的值进行注册。被static和readonly修饰的对象实际是这个hash code值。
DependencyProperty的建立过程:创建一个DependencyProperty实例并用它的CLR属性名和宿主类型名生成hash code,最后把hash code和DependencyProperty实例作为Key-Value对存入全局的名为PropertyFromName的HashTable中。
- 附加属性
附加属性是说一个属性本来不属于某个对象但由于某种需求而被后来附加上。也就是把对象放入一个特定环境后对象才具有的属性。
附加属性的本质就是依赖属性,二者仅在注册和包装器上有一点区别。以人在学校为例,人本身没有年纪Grade属性,但在学校时就被赋予了Grade,创建类School,代码如下:
public class School:DependencyObject { public static string GetGrade(DependencyObject obj) { return (string)obj.GetValue(GradeProperty); } public static void SetGrade(DependencyObject obj, string value) { obj.SetValue(GradeProperty, value); } public static readonly DependencyProperty GradeProperty = DependencyProperty.RegisterAttached("Grade", typeof(string), typeof(School)); }
可见GradeProperty就是一个DependencyProperty,唯一不同的就是注册附加属性使用的是RegisterAttached,对属性的包装使用的是两个函数。