为什么Dictionary的性能不好?
当然,这里说的性能不好主要指相对于Private Field-Public Property方式的性能而言。我们知道,.Net2.0中的Dictionary类,实际上是一个泛型的HashTable实现。因此,性能不佳的原因主要就是HashTable算法。FantasySoft在他的文章《解读Hashtable》中为我们回顾了HashTable算法的原理。纵观整个算法,主要有以下几个地方的性能损失:一个是hashcode的计算,即计算key的hashcode的代码;第二个是hashcode重复的问题,此时,需要进行一个附加的链表查询。并且,Dictionary或HashTable都是通用类型,他们的太多代码,在这里完全没有用到,而白白浪费了实例化他们的内存开销。
为什么不直接使用Private Field-Public Property方式生成Entity?
那么,既然Private Field-Public Property方式的性能绝对是最好的,为什么Teddy又要定义自定义的KeyValueCollection呢?直接的原因是,对于我的已有代码,尤其是emit生成代码来讲,将Dictionary类承载数据改成Private Field-Public Property方式,代码的改动幅度过大,为了避免运行时反射的性能损失,IEntity接口中的方法可能都将不再适合定义在基类Enitity<>中,而必须直接动态emit,也因此,虽然明知Private Field-Public Property的性能最好,但是,我并不愿意冒然向他缴械。也因此,如本文标题,Teddy开始尝试使用自定义的KeyValueCollection来代替Dictionary,希望能够获得和Private Field-Public Property相似的性能,但是又有Dictionary的那样的简单使用接口。
自定义KeyValueCollection!
考虑到我的KeyValueCollection只是用于持久化内部的数据承载,我不需要太大的通用性,只需要必要的接口就好,我将KeyValueCollection类定义如下:
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace Ilungasoft.Helper.Data
{
public class KeyValueCollection
{
private string[] keys;
private object[] values;
private KeyValueCollection()
{
}
public KeyValueCollection(params PropertyInfo[] pis)
{
if (pis != null)
{
keys = new string[pis.Length];
values = new object[pis.Length];
for (int i = 0; i < pis.Length; i++)
{
keys[i] = pis[i].Name;
values[i] = typeof(Util).GetMethod("DefaultValue", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public).MakeGenericMethod(pis[i].PropertyType).Invoke(null, null);
}
}
else
{
keys = new string[0];
values = new object[0];
}
}
public string[] GetKeys(params string[] exceptKeys)
{
if (exceptKeys != null && exceptKeys.Length > 0)
{
int retKeyCount = keys.Length - exceptKeys.Length;
if (retKeyCount <= 0)
{
return new string[0];
}
List<string> retKeys = new List<string>(retKeyCount);
foreach (string key in keys)
{
bool isExcept = false;
foreach (string exceptMember in exceptKeys)
{
if (key.Equals(exceptMember))
{
isExcept = true;
break;
}
}
if (!isExcept)
{
retKeys.Add(key);
}
}
return retKeys.ToArray();
}
else
{
return keys;
}
}
public object[] GetValues(params string[] exceptKeys)
{
if (exceptKeys != null && exceptKeys.Length > 0)
{
int retValueCount = keys.Length - exceptKeys.Length;
if (retValueCount <= 0)
{
return new object[0];
}
List<object> retValues = new List<object>(retValueCount);
for (int i = 0; i < keys.Length; i++)
{
bool isExcept = false;
foreach (string exceptMember in exceptKeys)
{
if (keys[i].Equals(exceptMember))
{
isExcept = true;
break;
}
}
if (!isExcept)
{
retValues.Add(values[i]);
}
}
return retValues.ToArray();
}
else
{
return values;
}
}
public KeyValueCollection Clone()
{
KeyValueCollection retKeyValues = new KeyValueCollection();
string [] cloneKeys = new string[keys.Length];
keys.CopyTo(cloneKeys, 0);
retKeyValues.keys = cloneKeys;
object[] cloneValues = new object[values.Length];
values.CopyTo(cloneValues, 0);
retKeyValues.values = cloneValues;
return retKeyValues;
}
public object this[int index]
{
get
{
return values[index];
}
set
{
values[index] = value;
}
}
}
}
接着,还要将原来对Dictionary的调用全都改成对新的类的调用,基类Entity<>的改动不太大,我就不列举了,运行时生成的Entity改动就比较大了。使用新的KeyValueCollection类后,实际生成的Entity的代码的范例列举如下(IAbout为需要用户定义的接口,static EntityFactory()为修改后的生成运行时About类的emit代码,最后的About为,实际上emit出来的类的等价hard code代码):
namespace Ilungasoft.Helper.TestApp.DomainObject2
{
public interface About: Ilungasoft.Helper.Data.IEntity
{
int ID { get; set; }
string Title { get; set; }
string Content { get; set; }
bool Deletable { get; set; }
int Order { get; set; }
}
}
{
//create dynamic IEntity Assembly & Type through Emit
if (assBuilder == null)
{
AssemblyName assName = new AssemblyName();
assName.Name = DYNAMIC_ENTITY_NAMESPACE;
assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.Run);
}
if (modBuilder == null)
{
modBuilder = assBuilder.DefineDynamicModule(DYNAMIC_ENTITY_NAMESPACE);
}
if (typeBuilder == null)
{
typeBuilder = modBuilder.DefineType(DYNAMIC_ENTITY_NAMESPACE + "." + typeof(IEntityType).FullName, TypeAttributes.Public);
typeBuilder.AddInterfaceImplementation(typeof(IEntityType));
Type keyValuesType = typeof(KeyValueCollection);
Type baseType = typeof(Entity<IEntityType>);
typeBuilder.SetParent(baseType);
PropertyInfo[] pis = typeof(IEntityType).GetProperties();
//define SetPropertyValue(string key, object val)
MethodInfo mi = typeof(IEntity).GetMethod("SetPropertyValue");
ParameterInfo[] paramInfos = mi.GetParameters();
int paramlength = paramInfos.Length;