加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 程序设计 > 正文

依赖属性的相关概念

发布时间:2020-05-24 08:18:43 所属栏目:程序设计 来源:互联网
导读:首先,我们分析一下依赖属性产生的一个原因: 由于一个类型的依赖属性大多数情况下都保持默认值,例如几百个button的fontsize一般都是一样的,甚至整个一个软件的fontsize都是一种。那么每个对象保存一个fontsize就显得没有那个必要了,我们必须通过一个方式来

首先,我们分析一下依赖属性产生的一个原因:

由于一个类型的依赖属性大多数情况下都保持默认值,例如几百个button的fontsize一般都是一样的,甚至整个一个软件的fontsize都是一种。那么每个对象保存一个fontsize就显得没有那个必要了,我们必须通过一个方式来保存这个属性,来让所有一个类的对象共享这个属性。我们的想法是把他做成一个静态的值。但是如果直接定义一个:

public static string name = string.empty;

但是我们可以看到这样的话,这个属性要是被设置的话,就会将所有的对象的这个属性全部都设置了。因此我们需要一个数据结构来存储这个值。

当要是当前对象设置了自己本身的这个属性的话,就在本地存储一个关于这个属性的值,来保证不会因为一个对象设置了这个值而导致所有的对象该属性都发生变化。

考虑到可能会涉及到属性的继承,所有我们还需要在数据结构中加入当前类型。

所以一个依赖属性应该包括以下内容:name,propertyType,ownerType,defaultValue。

我们用一个字典来存储这些所有的属性,这样的话就可以提高效率。

    class MyDependencyProperty
    {
        internal static Dictionary<object,MyDependencyProperty> RegisteredDps = new Dictionary<object,MyDependencyProperty>();
        internal string Name;
        internal object Value;
        internal object HashCode;
    
        private MyDependencyProperty(string name,Type propertyName,Type ownerType,object defaultValue)
        {
           this.Name = name;
          this.Value = defaultValue;
           this.HashCode = name.GetHashCode() ^ ownerType.GetHashCode();
       }

        public static MyDependencyProperty Register(string name,Type propertyType,object defaultValue)
       {
           MyDependencyProperty dp = new MyDependencyProperty(name,propertyType,ownerType,defaultValue);
           RegisteredDps.Add(dp.HashCode,dp);
          return dp;
       }
    }

我们设计了私有的构造函数,只通过Register方法来添加属性,这样可以保证一切的依赖属性都是经过我们的验证的。并且所有的依赖属性一旦都早出来就会立即添加入数据集合。

当我们一个类需要使用这个依赖属性,可以是如下方式

    class MyDependencyObject
    {
        public static readonly MyDependencyProperty NameProperty =
            MyDependencyProperty.Register("Name",typeof(string),typeof(MyDependencyObject),string.Empty);
        public object GetValue(MyDependencyProperty dp)
         {
             return MyDependencyProperty.RegisteredDps[dp.HashCode].Value;
         }
        public void SetValue(MyDependencyProperty dp,object value)
        {
            MyDependencyProperty.RegisteredDps[dp.HashCode].Value = value;
        }
        public string Name
        {
            get
           {
               return (string)GetValue(NameProperty);
           }
           set
           {
               SetValue(NameProperty,value);
           }
        }
    }

这样的话所有这个类的对象都不在拥有这个属性的内存,而是所有对象公用MyDependencyProperty类的静态数据集合RegisteredDps来存储所有的属性对象(MyDependencyProperty)。但是这个设计还有一定的问题,虽然解决了所有的依赖属性都存储在一个数据结构中的问题,但是没有解决同一个类的多个对象设置一个依赖属性时,所有的依赖属性都会被设置的问题。所以我们需要在本地添加一个数据集合,当我们设置这个属性的时候,就在这个数据集合中存储这个值,再次之后所有需要这个属性的值都是从本地存储的数据集合中找到的。

class MyDependencyObject
    {
        //这个list用来存储设置的依赖属性值,包括这个类的所有的依赖属性,都会在设置后存储在这里
        private List<EffectiveValueEntry> _effectiveValues = new List<EffectiveValueEntry>();
        public static readonly MyDependencyProperty NameProperty =
            MyDependencyProperty.Register("Name",string.Empty);
        public object GetValue(MyDependencyProperty dp)
         {
            //在数据结构中查找对应的依赖属性是否在本地有值。
             EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault((i) => i.PropertyIndex == dp.Index);
             if (effectiveValue.PropertyIndex != 0)
             {//如果获取的元素的PropertyIndex不等于0,说明本地存在这个属性的值
                 return effectiveValue.Value;
             }
             else
             {//本地不存在就去Dictionary中区查找。
                 return MyDependencyProperty.RegisteredDps[dp.HashCode].Value;
             }
         }
        public void SetValue(MyDependencyProperty dp,object value)
        {
            //在数据结构中查找对应的依赖属性是否在本地有值。
            EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault((i) => i.PropertyIndex == dp.Index);
            if (effectiveValue.PropertyIndex != 0)
            {//如果获取的元素的PropertyIndex不等于0,说明本地存在这个属性的值
               effectiveValue.Value = value;
            }
            else
            {//本地不存在,但是我们进行了Setvalue的调用,所以需要在本地创建一个该属性的数据结构
                effectiveValue = new EffectiveValueEntry() { PropertyIndex = dp.Index,Value = value };
                _effectiveValues.Add(effectiveValue);
            }
        }
        public string Name
        {
            get
           {
               return (string)GetValue(NameProperty);
           }
           set
           {
               SetValue(NameProperty,value);
           }
        }
    }
     internal struct EffectiveValueEntry
    {
       internal int PropertyIndex { get; set; }
    
        internal object Value { get; set; }
   }

当然依赖属性类中也需要添加属性的index。

class MyDependencyProperty
    {
        internal static Dictionary<object,MyDependencyProperty>();
        internal string Name;
        internal object Value;
        internal object HashCode;
        private static int globalIndex = 0;//所有的属性值的index将都会大于0
        internal int Index;
    
        private MyDependencyProperty(string name,object defaultValue)
        {
           this.Name = name;
           this.Value = defaultValue;
           this.HashCode = name.GetHashCode() ^ ownerType.GetHashCode();
       }

        public static MyDependencyProperty Register(string name,defaultValue);
           globalIndex++;
           dp.Index = globalIndex;
           RegisteredDps.Add(dp.HashCode,dp);
           return dp;
       }
    }

这样我们就解决了,当一个对象依赖属性设置后,其他该类对象的依赖属性也被设置的情况。 在DependencyObject加入了一个_effectiveValues,就是把所有修改过的DP都保存在EffectiveValueEntry里,这样,就可以达到只保存修改的属性,未修改过的属性仍然读取DP的默认值,优化了属性的储存。

但随着实际的使用,又一个问题暴露出来了。使用继承,子类可以重写父类的字段,换句话说,这个默认值应该是可以子类化的。那么怎么处理,子类重新注册一个DP,传入新的默认值?

其实很简单,我们在MyDependencyProperty中添加一个链表(List),存储这个依赖属性被定义它的类的子类对象重写时候的值。这样,因为每个依赖属性其实在clr加载的时候就已经伴随着这个类对象被构造或者调用,这个依赖属性作为一个静态数据结构就已经被在内存中创建了一份,然后其中保存着这个属性的默认值,当一个具体的对象修改自己本身的这个属性的时候,就在本地存储一个这个属性。(这里的本地是指在当前类的实例内存空间中存储一份属性的实例)。当我们需要访问这个属性的时候就先检查本地对象中是否有这个属性,没有就去访问这个静态的内存,那里面是属于所有这个类对象的依赖属性的值。

对于一个子类来说,他可以更新这个父类中关于这个依赖属性的初始值。

如下来做,在我们原有个MyDependencyProperty中添加一个list,这个list不是静态的,但我们使用这个类的子类,那么这个类的子类应该可以去重写这个依赖属性的默认值,当重写的时候,我们首先找到它从父类继承的MyDependencyProperty对象,然后在list中加入当前子类的类型和这个类型依赖属性的默认值。

当使用子类去获取依赖属性的值的时候,首先检查本地有没有设置好的属性值,没有就去MyDependencyProperty中找,找的时候首先检查list中有没有符合要求的,没有再去取默认值。

public class DependencyObject
    {
        private List<EffectiveValueEntry> _effectiveValues = new List<EffectiveValueEntry>();

        public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name",typeof(DependencyObject),"Name");

        public object GetValue(DependencyProperty dp)
        {//获取一个依赖属性的值,首先看本地是否有设定的值
            EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault((i) => i.PropertyIndex == dp.Index);
            if (effectiveValue.PropertyIndex != 0)
            {
                return effectiveValue.Value;
            }
            else
            {//没有本地设定值再去DependencyProperty中查找
                PropertyMetadata metadata;
                metadata = DependencyProperty.RegisteredDps[dp.HashCode].GetMetadata(this.GetType());
                return metadata.Value;
            }
        }

        public void SetValue(DependencyProperty dp,object value)
        {
            EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault((i) => i.PropertyIndex == dp.Index);
            if (effectiveValue.PropertyIndex != 0)
            {
                effectiveValue.Value = value;
            }
            else
            {
                effectiveValue = new EffectiveValueEntry() { PropertyIndex = dp.Index,Value = value };
                _effectiveValues.Add(effectiveValue);
            }
        }

        public string Name
        {
            get
            {
                return (string)GetValue(NameProperty);
            }
            set
            {
                SetValue(NameProperty,value);
            }
        }
    }

    public class SubDependencyObject : DependencyObject
    {
        static SubDependencyObject()
        {//子类重写这个依赖属性的默认值
            NameProperty.OverrideMetadata(typeof(SubDependencyObject),new PropertyMetadata("SubName"));
        }
    }

    public class DependencyProperty
    {
        private static int globalIndex = 0;
        internal static Dictionary<object,DependencyProperty> RegisteredDps = new Dictionary<object,DependencyProperty>();
        internal string Name;
        internal object Value;
        internal int Index;
        internal object HashCode;
        //_defaultMetadata表示这个依赖属性在定义类中的值
        private PropertyMetadata _defaultMetadata;
        //这个List存储所有的依赖属性可能值(子类重写的值)
        private List<PropertyMetadata> _metadataMap = new List<PropertyMetadata>();


        private DependencyProperty(string name,object defaultValue)
        {
            this.Name = name;
            this.Value = defaultValue;
            this.HashCode = name.GetHashCode() ^ ownerType.GetHashCode();
            //只有父类才会构造这个DependencyProperty对象,将父类的值作为默认值
            PropertyMetadata metadata = new PropertyMetadata(defaultValue) { Type = ownerType };
            _metadataMap.Add(metadata);
            _defaultMetadata = metadata;
        }

        public static DependencyProperty Register(string name,object defaultValue)
        {
            DependencyProperty dp = new DependencyProperty(name,defaultValue);
            globalIndex++;
            dp.Index = globalIndex;
            RegisteredDps.Add(dp.HashCode,dp);
            return dp;
        }
        /// <summary>
        /// 子类重写这个依赖属性
        /// </summary>
        public void OverrideMetadata(Type forType,PropertyMetadata metadata)
        {
            metadata.Type = forType;
            _metadataMap.Add(metadata);//将重写的值加入这个依赖属性的数据结构
        }

        public PropertyMetadata GetMetadata(Type type)
        {
            //判断list中是否有当前类的依赖属性值
            PropertyMetadata medatata = _metadataMap.FirstOrDefault((i) => i.Type == type) ??
                _metadataMap.FirstOrDefault((i) => type.IsSubclassOf(i.Type));
            if (medatata == null)
            {//没有就将默认值返回(子类未重写,返回父类定义的默认值)
                medatata = _defaultMetadata;
            }
            return medatata;//子类重写就返回子类的值。
        }
    }

    internal struct EffectiveValueEntry
    {
        internal int PropertyIndex { get; set; }

        internal object Value { get; set; }
    }

    public class PropertyMetadata
    {
        public Type Type { get; set; }
        public object Value { get; set; }
        //存储子类类型以及它对于的依赖属性的默认值
        public PropertyMetadata(object defaultValue)
        {
            this.Value = defaultValue;
        }
    }

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读