1.引言
由于string使用频繁,所以微软把它实现像值类型那样方便,甚至有string驻留机制,声明相同的字符串可以指向相同的托管堆内存,这样就可以提供内存的利用率。如果有一种class类型,它只是用来表示一些特征、一些描述信息、一些数据的存储,但是它们声明了就不会去改变这个对象里面的值,那么这种类型的对象就是值对象,这样的对象就可以共享,因为它不可变。
2.驻留方式
string有驻留方式,值对象就跟值类型和string一样,不可变,以至于值对象可以进行共享,如果值对象也能够以驻留方式进行创建,那就可以轻松实现共享,如下例子:
public class User { public string Name { get; set; } public string Password { get; set; } }
假设上面的User是一个值对象,需求:如果User对象的属性值相同,则保持相同的引用,如:现在要创建100个User对象,里面有99个Name和Password是相同的,那么我只要保持里面2个对象的引用即可,而不是保持100个对象的引用。通过提问,在朋友的帮助下,整理出了如下答案:
/// <summary> /// 值对象基类 /// </summary> public class ValueObject { protected static Dictionary<Dictionary<string, object>, List<Guid>> instances = new Dictionary<Dictionary<string, object>, List<Guid>>(); protected Dictionary<string, object> data = new Dictionary<string, object>(); protected Guid instanceId; protected ValueObject() { instanceId = Guid.NewGuid(); instances[data] = new List<Guid> { instanceId }; Console.WriteLine("创建对象:{0}", instanceId); } ~ValueObject() { var item = instances.SingleOrDefault(x => x.Value.Contains(instanceId)); item.Value.Remove(instanceId); if (item.Value.Count == 0) instances.Remove(item.Key); Console.WriteLine("释放资源ID: {0}.", instanceId); Console.WriteLine("当前还有 {0}个对象", instances.Count); //System.Diagnostics.Trace.WriteLine("释放资源ID:"+instanceId); } protected T getter<T>(string name) { T pv = default(T); var find = instances.SingleOrDefault(x => x.Value.Contains(instanceId)).Key; if (find.ContainsKey(name)) pv = (T)find[name]; return pv; } protected void setter<T>(string name, T value) { data[name] = value; reset(); } private void reset() { var query = instances.Where(w => !w.Value.Contains(instanceId)); var find = query.FirstOrDefault(f => f.Key.All(a => data.ContainsKey(a.Key) && data[a.Key] == a.Value)); if (find.Key != null) { instances.Remove(instances.FirstOrDefault(f => f.Value.Contains(instanceId)).Key); find.Value.Add(instanceId); } } }
下面看一个值对象User的实现:
public class User : ValueObject { public string Name { get { return getter<string>("Name"); } set { setter<string>("Name", value); } } public string Password { get { return getter<string>("Password"); } set { setter<string>("Password", value); } } }
调用如下:
static void Main(string[] args) { fu(); GC.Collect(); } static void fu() { List<User> list = new List<User>() { new User() { Name="qlin",Password = "1234" }, new User() { Name="qlin",Password = "1234" }, new User() { Name="lin",Password = "123456"}, new User() { Password = "12345" } }; }
为了说明,我们为User加上 判断:
public class User : ValueObject { public string Name { get { return getter<string>("Name"); } set { setter<string>("Name", value); } } public string Password { get { return getter<string>("Password"); } set { setter<string>("Password", value); } } public override bool Equals(object obj) { if (!(obj is User)) return false; return (obj as User).Name == Name && (obj as User).Password == Password; } public override int GetHashCode() { return instances.SingleOrDefault(x => x.Value.Contains(instanceId)).Key.GetHashCode(); } public static bool operator ==(User u1, User u2) { return u1.Equals(u2); } public static bool operator !=(User u1, User u2) { return !u1.Equals(u2); } public override string ToString() { return string.Format("[name = {0}, password = {1}, instanceid = {2}, hashcode = {3}]", Name, Password, instanceId, GetHashCode()); } }
测试调用如下:
class Program { static void Main(string[] args) { fu(); GC.Collect(); } static void fu() { var u1 = new User() { Name = "qlin", Password = "1234" }; var u2 = new User() { Name = "qlin", Password = "1234" }; var u3 = new User() { Name = "lin", Password = "123456" }; var u4 = new User() { Password = "123456" }; Console.WriteLine(u1 == u2); Console.WriteLine(u1 == u3); Console.WriteLine(u3 == u4); u4.Name = "lin"; Console.WriteLine(u3 == u4); } }
3.小结
这种驻留方式创建对象,大部分系统用不上,只是提供一个示例实现而已,没有太多的优化,但是有一些值得注意的地方
- 值对象本身只是一些数据而已,即一般都是属性,所以就在属性上做文章。
- 值对象创建了之后,都要缓存起来,以便以后创建相同的直接返回,查考静态变量。
- 以前的值对象都都被缓存起来,当对象成为垃圾对象时,GC会调用对象的析构函数,通过析构函数来清理当前对象相关的缓存数据。
很多朋友提出了弱引用来创建,这是一个飞常好的方法。有啥好方法请留言,谢谢!
作者:Qlin
出处:http://www.cnblogs.com/qqlin/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。