WPF的依赖属性是怎么节约内存的
WPF升级了CLR的属性系统,加入了依赖属性和附加属性。依赖属性的使用有很多好处,其中有两点是我认为最为亮眼的:
1)节省内存的开销;
2)属性值可以通过Binding依赖于其它对象上,这就使得我的数据源一变动全部依赖于此数据源的依赖属性全部进行更新。
第二点开发过WPF或者SilverLight应用程序都能无比畅快地感受它带来的好处,而在节省内存这个亮点上我们也行未能深刻地感受它带来的心理上的爽快,本人试着简单地说明依赖属性到底是怎么样为我们节省内存的。
我们先来看看传统的CLR属性,先来定义个人的类Person,简单点,只包含了Name和Coutry属性,Country默认是China。
public class Person
{
public Person()
{
Country = "China";
}
public string Name { get; set; }
public string Country { get; set; }
}
如果我们现在需要造10000个中国人,太简单了,在一个循环里实例化10000个Person就行了。如果你根本不关心程序占用内存的消耗你当然不会心痛,否则你就会喊坑爹了,因为这10000个中国人的Country都是China,但内存必须为每个China开辟空间来存放,这实在暴殄天物啊!好吧,到这里你应该知道依赖属性靠节省内存这个亮点都可以闪亮登场了,虽然我们最爽的还是前面说的第一点的一变全变的功能。
谈依赖属性DependencyProperty就绕不过要谈谈依赖对象DependencyObject,这还得从Dependency提供的两个方法说起,GetValue和SetValue。
public class DependencyObject :DispatcherObject
{
public object GetValue(DependencyProperty dp)
{
}
public void SetValue(DependencyProperty dp,object value)
{
}
}
原来DependencyProperty本身不提供获取和设置依赖属性值的操作,而是由DependencyObject来负责。DependencyObject是使用GetValue和SetValue方法通过DependencyProperty实例实现对属性值的读取和保存。注意这里所说的读取和保存的属性值其实就是DependencyProperty对应的属性值,也就是SetValue方法第二个参数object类型的value,它和DependencyProperty实例对象本身是不同的东西。如果还不能很好理解,没关系,继续往下看就明白了,我们下面就升级下Person的Country属性,使之成为真正的依赖属性。
public class Person : DependencyObject
{
/// <summary>
/// 依赖属性
/// </summary>
public static readonly DependencyProperty CountryProperty = DependencyProperty.Register("Country", typeof(string), typeof(Person), new PropertyMetadata("China"), new ValidateValueCallback(CountryValidateValueCallback));
/// <summary>
/// 依赖属性的CLR属性包装器
/// </summary>
public string Country
{
get { return (string)GetValue(CountryProperty); }
set { SetValue(CountryProperty, value); }
}
/// <summary>
/// 属性值验证
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool CountryValidateValueCallback(object value)
{
string country = (string)value;
if (country.Equals("Japan")) return false;
return true;
}
/// <summary>
/// Name是一个CLR属性
/// </summary>
public string Name { get; set; }
}
上面创建的一个依赖属性CountryProperty是用到DependencyProperty.Register最完全的参数的重载方法,关于这个方法里面各个参数的详情不是本文讨论的重点,如果你需要详细了解,可以阅读Clingingboy写的关于依赖属性的文章。DependencyProperty.Register的一个参数”Country”用来指明哪个CLR属性作为这个依赖属性的包装器,另外还需要使用这个它的HashCode,这点后面会讲到。第二个参数“typeof(string)”指明此依赖属性存储的的什么类型的值。第三个参数“typeof(Person)”用来指明此依赖属性宿主是什么类型,也需要使用它的HashCode。第四个参数“new PropertyMetadata("China")”可以指定依赖属性读取值的默认值,这里默认是“China”,第五个参数“new ValidateValueCallback(CountryValidateValueCallback)”,它指定验证值的方法,好吧,我们这个验证值的方法就把Japan先排除吧,凡是Country赋值为Japan就会抛出异常。
这里定义的CountryProperty是一个static,我们知道静态对象里面的值都是一变全变的,也就是我在一处修改了静态对象,所有引用这个静态对象的地方都会改变,这说明CountryProperty不适合来保存value这个属性值,否则假如我实例化10000个Person,这10000个Person都共用一个CountryProperty,到底用来保存哪个Person对象的Country呢?
上面实例化一个DependencyProperty没有使用new关键字,而是使用DependencyProperty.Register这个静态方法,这个静态方法大有乾坤,下面我们重点分析下这个静态方法。
我们先看看DependencyProperty类中DependencyProperty.Register的源码,这里为了阅读方便将干扰我们阅读的部分代码去掉了:
public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata, ValidateValueCallback validateValueCallback)
{
RegisterParameterValidation(name, propertyType, ownerType);
DependencyProperty property = RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback);
return property;
}
RegisterParameterValidation(name, propertyType, ownerType)方法验证第一个、第二个和第三个参数是否为null,任何一个为null都将抛出异常,说明这三个参数在调用Register方法是必传的。
DependencyProperty property = RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback),这行告诉我们内部又使用了RegisterCommon方法来实例化DependencyProperty对象。我们在来看看RegisterCommon方法的关键源码,同样只保留了关键的源码:
private static DependencyProperty RegisterCommon(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateV
补充:Web开发 , ASP.Net ,