值對象共享


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會調用對象的析構函數,通過析構函數來清理當前對象相關的緩存數據。

很多朋友提出了弱引用來創建,這是一個飛常好的方法。有啥好方法請留言,謝謝!

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM