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会调用对象的析构函数,通过析构函数来清理当前对象相关的缓存数据。

很多朋友提出了弱引用来创建,这是一个飞常好的方法。有啥好方法请留言,谢谢!

 

 

posted on 2012-12-27 13:11  Qlin  阅读(1497)  评论(28编辑  收藏  举报