远古的 WPF 框架开发的大佬们认为没有任何业务的开发者需要用到超过 65534 个依赖属性和附加属性,为了节省内存空间就限制了所有的依赖属性和附加属性的定义总和加起来不能大于等于 65535 个
似乎大家可能对 65535 个依赖属性的定义量没有概念,这么说,即使只是将这些依赖属性定义出来,那代码的 cs 文件的大小也差不多有 20MB 这么大。敲黑板,这里的 65535 个依赖属性的定义量,指的是在代码里面定义 65535 个依赖属性或附加属性,指的是编写的代码,而和应用运行过程中创建多少个对象毫无关系
接下来咱来写一点有趣的代码来测试 WPF 的这个行为,先新建两个项目,一个是名为 LunallherbeanalLerejucahallyeler 的 WPF 项目,另一个是名为 KeeheekairbiQahairnairdacem 的控制台项目。将由控制台项目 KeeheekairbiQahairnairdacem 生成超过 65535 个依赖属性的定义的代码,用来给 LunallherbeanalLerejucahallyeler 项目引用
由于如此多的定义在一个类型里面,将会触发 CLR 层的异常,如果生成的代码都放在 MainWindow 类型里面,运行过程中大家将会看到如下异常
为了能够让这个逗比代码能够跑起来,于是接下来我拆分为 10 个类型,每个类型里面放入 7000 个依赖属性
而由于分了类型了,众所周知,依赖属性的定义默认放的是静态的属性。而静态的属性是由静态构造函数初始化的,静态构造函数又是需要在逻辑碰到静态字段等情况下才会执行的,这就意味着还需要给这 10 个类型投点毒,让这些类型的静态构造函数能够正确执行,从而创建出足够的依赖属性定义的静态字段
接着为了显示出当前 WPF 框架里面注册的依赖属性数量,我还使用反射在界面显示当前的注册的依赖属性数量,如下面代码
以上代码的 TextBlock 是定义在 MainWindow.xaml.cs 的控件,界面代码如下
接着编写 KeeheekairbiQahairnairdacem 控制台项目的代码,生成足够数量的依赖属性的定义,这部分代码没有什么难度,我就不贴在博客里面,大家可以在本文末尾找到全部代码的下载方法
这时候运行 WPF 项目,即可看到大概如下的异常
这就是因为定义的依赖属性超过了最大数量的限制,在 WPF 里面的 DependencyProperty 限制了最大的依赖属性和附加属性加起来的总数量,代码如下
以上的 GlobalIndexCount 静态字段是用来表示当前定义的依赖属性或附加属性是第几个加入到 WPF 框架里面的,如果超过了 Flags.GlobalIndexMask 数量个,那将会抛出异常。这里的 GlobalIndexMask 就是 65535 个
大家都知道,在 WPF 里面的依赖属性和附加属性都是存放在类型里面的字典里面,而字典的查找是依赖于哈希算法的。为了能够让依赖属性既有足够快的查找速度且又对人类友好,于是定义了依赖属性包含了属性名字符串,还包含了从 GlobalIndexCount 静态字段算出的 GlobalIndex 索引值。通过 GlobalIndexCount 确保每个依赖属性定义都有独立且不重复的 GlobalIndex 索引值,如此即可实现依赖属性字典通过 int 作为 key 提升其性能
更具体一点,讲 WPF 的依赖属性和附加属性在底层使用字典存放是片面的,属于思想正确但具体实现不正确,具体的实现是在 WPF 底层存放了一个有序数组,这个数组通过上文说讲的依赖属性的 GlobalIndex 作为排序依据,如此即可通过折半查找算法快速找到命中的依赖属性对应的值
本文以上的代码放在github 和 gitee 欢迎访问
可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码
获取代码之后,进入 LunallherbeanalLerejucahallyeler 文件夹
先运行 KeeheekairbiQahairnairdacem 项目一次,让其创建 LunallherbeanalLerejucahallyeler 所使用的代码,接着再运行 LunallherbeanalLerejucahallyeler 项目,即可看到本文的效果
原文链接: http://blog.lindexi.com/post/WPF-%E5%86%B7%E7%9F%A5%E8%AF%86-%E5%AE%9A%E4%B9%89%E4%BE%9D%E8%B5%96%E5%B1%9E%E6%80%A7%E7%9A%84%E6%9C%80%E5%A4%A7%E6%95%B0%E9%87%8F%E6%98%AF-65534-%E4%B8%AA
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
欢迎转载、使用、重新发布,但务必保留文章署名 林德熙 (包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我 联系。