zencart wordpress,郑州网站建设及优化,宝安中心医院是什么级别,wordpress加载图片的速度前言在应用开发中#xff0c;通常都会涉及各种 POJO/POCO 实体类#xff08;DO, DTO, BO, VO#xff09;的编写#xff0c;有时这些实体类还需要实现 INotifyPropertyChanged 接口以支持属性变更通知#xff0c;一般我们都会手写这些代码或者通过工具根据数据库表定义抑或… 前言在应用开发中通常都会涉及各种 POJO/POCO 实体类DO, DTO, BO, VO的编写有时这些实体类还需要实现 INotifyPropertyChanged 接口以支持属性变更通知一般我们都会手写这些代码或者通过工具根据数据库表定义抑或别的什么模板、映射文件之类的来静态生成它们。但是在业务实现中往往伴随着诸如“如何简单且高效的获取某个实体实例有哪些属性发生过变更”、“变更后的值是什么”这样的问题而大致的解决方法有由实体容器来跟踪实例的属性变更改造实体类譬如继承特定实体基类在基类中实现这些基础构造。方法(1)需要配合一整套架构设计来提供支撑也不是专为解决上述实体类的问题而设并且实现和使用也都不够简单高效故此略过不表。接下来我将通过几篇文章来详细阐述这些问题的来由以及解决方案并给出完整的代码实现以及性能比对测试。关于源码下面将要介绍的所有代码均位于我们的开源系列项目地址https://github.com/Zongsoft项目主要采用 LGPL 2.1授权协议欢迎大家参与并使用请遵照授权协议。本文相关的源码位于其中 Zongsoft.CoreLibrary 项目的 feature-data 分支https://github.com/Zongsoft/Zongsoft.CoreLibrary/tree/feature-data及其中的 /samples/Zongsoft.Samples.Entities 范例项目由于目前我正在忙着造 Zongsoft.Data 数据引擎这个轮子不排除后面介绍到的代码会有一些调整待该项目完成后这些代码亦会合并到 master 分支中敬请留意。基础版本万里长城也是从第一块砖头开始磊起来的就让我们来搬第一块砖吧public class User{private uint _userId;private string _name;// 传统写法public uint UserId {get {return _userId;}set {_userId value;}}// C# 7.0 语法public string Name {get _name;set _name value;}// 懒汉写法仅限不需要操作成员字段的场景public string Namespace {get;set;}}以上代码特地用了三种编码方式它们被C#编译器生成的IL没有模式上的不同故而性能没有任何区别大家根据自己的口味采用某种即可因为我们的源码由于历史原因可能会有一些混写在此一并做个展示而已。由于业务需要我们希望实体类能支持属性变更通知即让它支持 INotifyPropertyChanged 接口这么简单的需求当然不在话下public class User : INotifyPropertyChanged{public event PropertyChangedEventHandler PropertyChanged;private uint _userId;private string _name;public uint UserId {get _userId;set {if(_userId value)return;_userId value;this.OnPropertyChanged(UserId); // 传统写法}}public string Name {get _name;set {if(_name value)return;_name value;this.OnPropertyChanged(nameof(Name)); // nameof 为 C# 7.0 新增操作符}}protected virtual void OnPropertyChanged(string propertyName){// 注意 ?. 为 C# 7.0 新增操作符this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}}一切看起来是那么完美但是当我们写了几个这样的实体类尤其是有些实体类的属性还不少时体验就有点糟糕了。自然我们会想到写个实体基类来实现属性变更通知的基础构造当然在某些特定场景也可以通过工具来生成类似上面这样的C#实体类文件但工具生成的方式有一定局限性并且不易维护譬如需要在生成的代码基础上进行特定改造在此不再赘述。实体基类在进行基础类库或API设计的时候我有个建议从应用场景开始。具体的作法是先尝试编写使用这些API的应用代码待各种应用场景的使用代码基本都完成后API接口也就自然而然的确定了。譬如在我们这个需求中我希望这么去使用实体基类public class User : ModelBase{private uint _userId;private string _name;public uint UserId {get _userId;set this.SetPropertyValue(nameof(UserId), ref _userId, value);}public string Name {get _name;set this.SetPropertyValue(nameof(Name), ref _name, value);}}有了这样的实体基类后增强了功能后代码依然如第一块砖的“基础版本”一样简洁真是高兴啊但这就够了么能不能把具体实体类里面的成员字段也省了交给基类来处理呢嗯有点意思试着写下应用场景代码public class User : ModelBase{public uint UserId {get (uint)this.GetPropertyValue(nameof(UserId));set this.SetPropertyValue(nameof(UserId), value);}}看起来棒极了代码变得更简洁了真是天才啊淡定丧心病狂的 C# 设计者似乎看到了这种普遍的需求于是在 C# 5 中增加了 System.Runtime.CompilerServices.CallerMemberNameAttribute 自定义标记C# 编译器将自动把调用者名字生成出来传递给加注了该标记的参数因此这样的代码还可以继续简化public class User : ModelBase{public uint UserId {get (uint)this.GetPropertyValue();set this.SetPropertyValue(value);}}但是属性的 getter 里面的那个类型强制转换怎么看都像是一朵“乌云”啊能不能把它也去掉呢嗯利用C#的泛型类型推断可以完美解决它继续强势进化public class User : ModelBase{public uint UserId {get this.GetPropertyValue(() this.UserId);set this.SetPropertyValue(() this.UserId, value);}}哇喔有点小崇拜自己了这代码漂亮的一批至此实体基类的API接口基本确定已经迫不及待想要去实现它了。提示由于采用 CallerMemberNameAttribute 自定义标记的参数会导致 C# 编译器要求该参数必需有默认值因此有些 SetPropertyValue(...) 方法重载版本中 propertyName 参数需要位于参数集的最后为了与上面的范例代码对应就省略了这些参数的标记并保持与原有范例相同的签名设计。using System;using System.Linq.Expressions;public class ModelBase : INotifyPropertyChanged{public event PropertyChangedEventHandler PropertyChanged;protected object GetPropertyValue([CallerMemberName]string propertyName null);protected T GetPropertyValueT(ExpressionFuncT property);protected void SetPropertyValueT(string propertyName, ref T field, T value);protected void SetPropertyValueT(string propertyName, T value);protected void SetPropertyValueT(ExpressionFuncT property, T value);}实体基类的实现主要思路就是采用字典来记录各属性的变更值有了这个基础要继续增加诸如“获取哪些属性发生过变更”之类的需求自然就很容易了public class ModelBase : INotifyPropertyChanged{// other memberspublic bool HasChanges(params string[] propertyNames);public IDictionarystring, object GetChangedPropertys();}具体的代码就不在这里贴出了有兴趣的可以参考https://github.com/Zongsoft/Zongsoft.CoreLibrary/blob/master/src/Common/ModelBase.cs从功能角度上看目前的设计还是不错的。但是某些方法的设计有严重性能缺陷的主要有以下几点每次读写属性都会解析 Lambda 表达式的操作会产生巨大的性能损耗采用字典来保存实体属性值的设计机制会导致值类型的属性读写反复被装箱(Boxing)、拆箱(Unboxing)字典的读写效率也远低于直接操作成员字段的语言原语方式。综上所述虽然目前方案有性能缺陷但应对一般场景其实是没有问题的而且功能和易用性方面都是很好的但是性能对于后台程序猿而言犹如悬在头顶的达摩克利斯之剑这正是这个系列文章要最终解决的问题。在此之前如果大家有关于这个问题的性能优化方案欢迎关注我们的公众号Zongsoft留言讨论原文地址https://mp.weixin.qq.com/s/MmBU53hxTDHzhRGoj3DjRg.NET社区新闻深度好文欢迎访问公众号文章汇总 http://www.csharpkit.com