最近最烦的事莫过于为我的ERP提速,由于学习习惯不好,WPF走了很多弯路,导致客户端性能实在惨不忍睹。最近做得最多的事就是找到WPF 的“档位器”,老牛拉的破车怎么也得上个4档跑个80码吧。
要把这破车整好,得先洗洗发动机吧,Task并行任务+Dispatcher的Invoke是解决的好办法,让你的多核CPU跑起来就象是发动机开齐了四缸一样,速度上来了。
要让车跑得快,路面很关键,地面跑个80码好象得把驾照送给sir了吧,那我们就上嘉定的F1去飙吧。WFP的数据绑定玩不好,那可是在S20上过外环隧道一样,对绑定的对象的反射可是又占内存又耗时哟,MSDN说得好,绑定1000个有一个属性的对象比绑定1个有1000个属性的对象快N倍,N年前就看到了这句话,理解是“将你的对象设计得更小”,可不得不接受的现实是,一个数据库表映射上来的实体可能就有几十个字段,再加上我们有N个这样的实体,哟,一路上都是如黄河决堤一般的反“射”,射得是路面湿滑一车是水,哪还跑得起来嘛!
周未嘛,通宵写点代码是很快乐的事,原本只是受“并行-双飞”的刺激有了点快感,可数据读出来后,显示老是要闪一次(WPF在边反射边显示嘛,慢得都看得出来了),对于这现象,我更多的是怀念用MFC时的快速,痛恨眼前的一切。这么多年过去了,我真不记得写程序还有什么“灵感”存在了,都是按那一堆的理论老实工作,可是今天我真的体会到了!
开始也只是想让这显示的数据做一个交换,假假的消除这个“闪”,或许是我和老婆是八年抗战才修得的正果吧,我讨厌无论是真还是假的“闪”,就在Coding的一时,这久违的“灵感”来了,为什么不设计一个只 有一个属性的东东呢,开工吧!代码很快完成!完成时其实还没现在这兴奋的快感,因为WPF还是要反射,只是射的次数少了而已,嗯,无奈时看MSDN。一个从来不用的绑定语法呈现在我眼前: 可以在 Path 子句中混合索引器和子属性;例如,Path=ShoppingCart.ShippingInfo[MailingAddress,Street]. 天哪,这就是我想要的!
实现一个IDictionary<string, string>字典类型的属性,并且它实现成依赖属性(这是不需要反射的,它直接查依赖属性表即可),而这个属性的类型WPF知道它是字典,也不需要反射就可以使用,那我们只要在这个字典的实现中用代码的方式来取属性就不就终结了反射了吗? O'YEAH,何况这个字典可以是任何CLR类型,那这个索引器实现成文本,那可以直接应用现在的实体的匿名取值,如果在性能上要更上一层楼,用数字还做SWITCH,那子弹就飞完了^_^!
代码很快就实现了,真的,它的速度就和当年用MFC时一样快,界面却比MFC时代漂亮多了,这才是真的进步! 成功了! 贴代码(好象除了十年前完成那个C++实现的B树模板类之外,没这个冲动了)!
using System;
using System.Collections;
using System.Windows;
using System.Collections.Generic;
using Agebull.Common.Entity.Generic;
namespace Agebull.Common.Client.Windows.DataViews
{
/// <summary>
/// 无需反射的依赖实体对象
/// </summary>
/// <remarks>使用时要构造这个对象,因为依赖对象是不能用于WCF的,切记!,但这代码量对于性能的极速提升是绝对值得的</remarks>
public class DependencyEntity : DependencyObjectEx
{
/// <summary>
/// 绑定选择的属性
/// </summary>
public readonly static DependencyProperty IsSelectProperty;
/// <summary>
/// 包装的字典对象属性
/// </summary>
public readonly static DependencyProperty DictionaryProperty;
/// <summary>
/// 构造属性
/// </summary>
static DependencyEntity()
{
IsSelectProperty = RegisterProperty<bool?>("IsSelect", typeof(DependencyEntity), false, OnSelectedChanged);
DictionaryProperty = RegisterProperty<IDictionary<string, string>>("Dictionary", typeof(DependencyEntity));
}
/// <summary>
/// 同步选择到普通的实体对象
/// </summary>
/// <param name="d"></param>
/// <param name="e"></param>
static void OnSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DependencyEntity navigate = d as DependencyEntity;
if (navigate == null || Equals(e.NewValue, e.OldValue))
return;
navigate.entity.P_Infomation.IsSelect2 = (bool?)e.NewValue;
}
/// <summary>
/// 普通的实体对象
/// </summary>
/// <remarks>这个类型是我自己的实现,就不公开了</remarks>
private EntityBase entity;
/// <summary>
/// 构造
/// </summary>
public DependencyEntity()
{
this.SetValue(DictionaryProperty, Dictionary = new NavigateEntityItem());
}
/// <summary>
/// 一次性构造
/// </summary>
/// <param name="e"></param>
public DependencyEntity(IEntity e)
{
entity = e as EntityBase;
Dictionary = new NavigateEntityItem { Entity = entity };
}
/// <summary>
/// 交换数据,防止在集合时因为处理删除和加入而进行的UI渲染
/// </summary>
/// <param name="e"></param>
public void Switch(IEntity e)
{
entity = e as EntityBase;
Dictionary = new NavigateEntityItem { Entity = entity };
}
/// <summary>
/// 字典属性封装,便于访问(尤其是跨线程访问
/// </summary>
public NavigateEntityItem Dictionary
{
get
{
if (Dispatcher.CheckAccess())
return (NavigateEntityItem)GetValue(DictionaryProperty);
else
return (NavigateEntityItem)Dispatcher.Invoke(new Func<object>(()=>GetValue(DictionaryProperty)));
}
private set
{
if (Dispatcher.CheckAccess())
SetValue(DictionaryProperty, value);
else
Dispatcher.Invoke(new Action(() => SetValue(DictionaryProperty, value)));
}
}
}
/// <summary>
/// 字典实现
/// </summary>
/// <remarks>除了</remarks>
public class NavigateEntityItem : IDictionary<string, string>
{
/// <summary>
/// 这个最重要,它是对普通实体的访问通道
/// </summary>
/// <remarks>
/// 这个类型是我自己的实现,就不公开了,
/// 你在this[]中可以自由实现我们想要的东东,最好是用依赖的思想,设计一个统一接口,
/// 并实现这个依赖的注入(也就是绑定前正确构造就成了)
/// </remarks>
public EntityBase Entity
{
get;
set;
}
/// <summary>
/// 取值
/// </summary>
/// <param name="key"></param>
/// <returns>绑定要的值</returns>
/// <remarks>
/// 这是重点,是不反射和WPF绑定交互的命门,通过它普通实体对象就可以以一等公民的方式和绑定交互了,被反射是二等公民啦!
/// 咦,是不是用老土的DataTable也可以达到这效果?有兴趣者请考证
/// </remarks>
public string this[string key]
{
get
{
return Entity.GetUserString(key, null);
}
set
{
Entity.SetValue(key,value);
}
}
#region 接口实现,事实上我们应该永远不会用它
/// <summary>
/// 静态实现一个单例以防止实现接口时返回null而出问题,节省内在
/// </summary>
private static readonly Dictionary<string, string> values = new Dictionary<string, string>();
IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator()
{
return values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return values.GetEnumerator();
}
void ICollection<KeyValuePair<string, string>>.Add(KeyValuePair<string, string> item)
{
}
void ICollection<KeyValuePair<string, string>>.Clear()
{
}
bool ICollection<KeyValuePair<string, string>>.Contains(KeyValuePair<string, string> item)
{
return true;
}
void ICollection<KeyValuePair<string, string>>.CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
{
}
bool ICollection<KeyValuePair<string, string>>.Remove(KeyValuePair<string, string> item)
{
return true;
}
int ICollection<KeyValuePair<string, string>>.Count
{
get
{
return 1000;
}
}
bool ICollection<KeyValuePair<string, string>>.IsReadOnly
{
get
{
return true;
}
}
bool IDictionary<string, string>.ContainsKey(string key)
{
return true;
}
void IDictionary<string, string>.Add(string key, string value)
{
}
bool IDictionary<string, string>.Remove(string key)
{
return true;
}
/// <summary>
/// 试图取值,应该也是没用的
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
bool IDictionary<string, string>.TryGetValue(string key, out string value)
{
value = Entity.GetUserString(key, null);
return true;
}
ICollection<string> IDictionary<string, string>.Keys
{
get
{
return values.Keys;
}
}
ICollection<string> IDictionary<string, string>.Values
{
get
{
return values.Values;
}
}
#endregion
}
}