• WPF 依赖属性


        我们知道,在我们的C#中 有属性这个概念,也有字段这个概念。关于字段,我们知道当不被static修饰的时候,会在内存中真正的去申请相应的内存空间。每个字段都会有一块自己的空间。而属性是什么? 属性本身其实是方法,只是C#提供了语法糖是的写法变的更简单而已。
    比如 我们的类如下
     public class Person
        {
            private string name;
            private string sex;
            public int age;
            public string Name
            {
                get { return name; }
                set { name = value ; }
            }
            public string Email
            {
                get;
                set;
            }     
        }
    对应的IL如下
    我们可以清晰的看到 Email 这个属性 本质上只是 get_Email和set_Email这两个方法,并且我们会发现,当我们没有手动提供直接的字段作为背后的支持的话, 他会自动生成相关的“BackingField”--背后支持的字段,来做支持,有就是说也会申请相应的内存空间来存放 相应的字段。比如 Email 属性就生成的是 <Email>k_BackingField 这样的字段而已,连名字都起的相当贴切。
     
    这样 我们的属性和字段就应该相当清楚了。 概括起来就是 属性只是方法的简写(语法糖),背后支持仍然是真实存在的字段,需要真正的内存空间申请。
     
    那么,好,在标准的属性系统上,如果我们的一个类有100个属性,那么创建这样的一个实例,实际上属性共需要100个内存空间,如果创建 100个这样的对象,就需要100*100个空间。这样子会有什么不好吗? 这个例子还不够详细,不够贴切,可能还感受不到。
    举个更贴切的例子。
    我们的一个TextBox 类可能有100个属性,但其中真正经常会使用的也许就只有Text这一个属性。那其他99个属性也是要申请空间的。
    如果我们使用了100个TextBox对象,每个对象只是Text属性不同 其他的所有属性,都完全一样,你会不会觉得 为这所有的100个控件的不常用的99个属性 一共申请99*100个空间 有点浪费?我明知道你们是一模一样 保持一致的,却还只能给你申请空间来存一模一样的东西,是有那么点浪费。 不过现在电脑内存好像都挺大的,你可以懒得去管这些什么内存不内存的,(现实确实不去考虑这些)。但先别急,我们只是为了讲清楚知识点。也就是说,现有的属性系统 在某些情况下(大量实例,属性值相同),对于内存的开销有点大。
      我们来手动简略模拟一下WPF的依赖属性系统  如下
    public class DependencyProperty
        {
            public static Dictionary<Object, DependencyProperty> Dic = new Dictionary<object, DependencyProperty>();
            public string name;
            public object defaultvalue;
            public object Hashcode;
    
            private DependencyProperty(string name, object value, Type propType, Type owenerType)
            {
                this.name = name;
                this.defaultvalue = value;
                this.Hashcode = propType.GetHashCode() ^ owenerType.GetHashCode();
            }
            public static DependencyProperty Register(string name, object value, Type propType, Type owenerType)
            {
                DependencyProperty p = new DependencyProperty(name, value, propType, owenerType);
                Dic.Add(p.Hashcode, p);
                return p;
            }
        }
      public class DependencyObject
        {
            public static DependencyProperty NameProperty = DependencyProperty.Register("Name", "shenwei", typeof(string), typeof(DependencyObject));
            public static DependencyProperty AgeProperty = DependencyProperty.Register("Age", 23, typeof(int), typeof(DependencyObject));
    
            public Object GetValue(DependencyProperty d)
            {
                return DependencyProperty.Dic[d.Hashcode];
            }
            public void SetValue(DependencyProperty d, object value)
            {
                DependencyProperty.Dic[d.Hashcode].defaultvalue = value;    //现在的情况是  设置的属性值是默认值。 那接下来所有实例的这个属性的默认值都应该是这个。但是现在的代码 实际上一个实例修改 ,所有的实例都会修改。 那我们这边的setValue就不能这么写。我们还需要一个单独服务于 实例的字典。
            }
        }

     这个是我们模拟的第一步,目前我们所做的就是建立一个静态字典,然后向其中插入数据
     插入的值时一个 dependencyProperty对象,插入的键很有意思,是由DependencyProperty本身的 数据类型以及拥有这个属性的类的类型共同决定
     由这两者本身的hashcode异或组成了这个依赖属性的hashcode 作为键值存储在静态字典中。
     但是这样子,一个实例的属性弱发生变化,这个类的所有实例的这个以来属性都会发生变化,很显然这是 荒唐的。 这边只 是针对这个属性的默认值的情况。

     在DependencyObject里面添加了一个实例字典,来存储实例的属性值,避免了一个实例属性改,所有都改的尴尬。

     public class DependencyObject
        {
            public static DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(DependencyObject), "shenwei");
            public static DependencyProperty AgeProperty = DependencyProperty.Register("Age", typeof(int), typeof(DependencyObject), 23);
            //小旅行包
            private Dictionary<object, object> Dic_for_instance_propertyValue = new Dictionary<object, object>();    //键值就采用的DependencyProperty的键值,因为这个实例字典是属于实例自己的,所以无需担心重复。
            public object GetValue(DependencyProperty p)
            {
                if (Dic_for_instance_propertyValue.ContainsKey(p.hashcode))
                {
                    return Dic_for_instance_propertyValue[p.hashcode];   //返回实例的属性具体值
                }
                return DependencyProperty.Dic_Dps[p.hashcode].property_default_value;  //返回属性默认值
            }
            public void SetValue(DependencyProperty p, object pro_value)
            {
                Dic_for_instance_propertyValue[p.hashcode] = pro_value;
            }
            public string Name
            {
                get
                {
                    return this.GetValue(DependencyObject.NameProperty).ToString();
                }
    
                set
                {
                    SetValue(NameProperty, value);
                }
            }
            public int Age
            {
                get { return (Int32)(this.GetValue(DependencyObject.AgeProperty)); }
                set { SetValue(AgeProperty, value); }
            }
        }

     如果 依赖属性系统单单只是为了 优化一下内存,那其实个人认为 并没有什么必要这么大费周章,所以依赖属性的功能并不是体现在此,而是体现在属性更改通知等。
    我们现在添加属性更改通知功能

        public delegate void PropertyChangedCallBack();
        public class PropertyMetaData
        {
            public PropertyChangedCallBack PropertyChanged;
            public PropertyMetaData(PropertyChangedCallBack para)
            {
                PropertyChanged = para;
            }
        }

    然后,与DependencyProperty相结合,就可以实现属性更改通知功能了。详细我们也就不铺开陈述了。

    借助于依赖属性,WPF构建了强大的属性系统,可以支持数据绑定、样式、附加属性等功能。我们手动简单的模仿了一下以来属性系统的大致实现,当然,具体肯定和WPF的实现还有不小的偏差。

     
     
     
  • 相关阅读:
    8.8集训
    8.7集训
    8.6集训
    poj 2492
    埃氏筛法
    并查集板子
    2018级程序能力实训第二次上机考试
    网络流
    活动安排问题
    等价类
  • 原文地址:https://www.cnblogs.com/JasonShenW/p/4842005.html
Copyright © 2020-2023  润新知