C# 實值型別 與參考型別

來源:互聯網
上載者:User

標籤:

前言

一般來說,實值型別存於棧,參考型別存在於堆,實值型別轉化為參考型別叫Box, 參考型別轉為實值型別在Unbox, 最近看了一本書發現實值型別與參考型別的知識遠不止這些。

我發現在一下幾點我的理解一直是錯誤的:

錯誤1. struct是實值型別,它與System.Object沒有任何關係。

struct 直接基類是System.ValueType, 而它的基類就是Sysem.Object, 而且int, bool,等基本類型都是struct. 所以所有的C#類型都繼承自System.Object.

錯誤2. 實值型別轉換為介面類型不會裝箱

我們都知道object obj = 3 會裝箱; 其實IEquatable<int> obj = 3; 和前面的一樣也會裝箱。

錯誤3. struct類型的equals 不會裝箱。

不僅有裝箱,而且還有兩次!

錯誤4:裝箱,拆箱的記憶體分布。

裝箱後,會重新分配堆,除了相關的值,還有函數表指標等,記憶體對齊等,還有一個漏掉的是指向這個裝箱後對象的引用。

 

本文

namespace ConsoleApplication1{    // System.ValueType -> System.Object    struct Point2D    {        public int X { get; set; }        public int Y { get; set; }        // version 1        public override bool Equals(object obj)        {            if (!(obj is Point2D)) return false;            Point2D other = (Point2D)obj;            return X == other.X && Y == other.Y;        }        // version 2        public bool Equals(Point2D other)        {            return X == other.X && Y == other.Y;        }        // version 3        public static bool operator ==(Point2D a, Point2D b)        {            return a.Equals(b);        }        public static bool operator !=(Point2D a, Point2D b)        {            return !(a == b);        }    }    struct Point2D_V4 : IEquatable<Point2D_V4>    {        public int X { get; set; }        public int Y { get; set; }        // version 1        public override bool Equals(object obj)        {            if (!(obj is Point2D_V4)) return false;            Point2D_V4 other = (Point2D_V4)obj;            return X == other.X && Y == other.Y;        }        // version 2        public bool Equals(Point2D_V4 other)        {            return X == other.X && Y == other.Y;        }        // version 3        public static bool operator ==(Point2D_V4 a, Point2D_V4 b)        {            return a.Equals(b);        }        public static bool operator !=(Point2D_V4 a, Point2D_V4 b)        {            return !(a == b);        }    }    public class Employee    {        public string Name { get; set; }        // the hashcode is base on the its cotent        public override int GetHashCode()        {            return Name.GetHashCode();        }    }    class Program    {        static void Main(string[] args)        {            // version 1            Point2D a = new Point2D(), b = new Point2D();            //object test = b; // box b from value type to reference type            //a.Equals(b); // box a to invoke the virtual function Equals            // if we have 10,000,000 points, we will do 20,000,000 box operation, on 32-bit system, one box will allocate 16 Bytes.            // version 2            // How to avoid boxing override the equals.            //a.Equals(b); // no need box a or b this time.            // version 3            //bool isequal = a == b;// no boxing here.            // it seems ok now, but there is a edge case will cause boxing at CLR generic. we need implement Iequatable            //Point2D_V4 i = new Point2D_V4(), j = new Point2D_V4();            //IEquatable<Point2D_V4> k = i;// box occurs here, value type to interface requires boxing.            //i.Equals(j);// no box occurs            // once boxing, they are not have relationship to orignal type. so the best practice is let the value type immutable, like system.datetime.             //Point2D_V4 point = new Point2D_V4 { X = 5, Y = 7 };            //Point2D_V4 anotherPoint = new Point2D_V4 { X = 6, Y = 7 };            //IEquatable<Point2D_V4> equatable = point; //boxing occurs here            //equatable.Equals(anotherPoint); //returns false            //point.X = 6;            //point.Equals(anotherPoint); //returns true            //equatable.Equals(anotherPoint); //returns false, the box was not modified!            // then we need to talk about the hashcode, if you know about the hashkey in datastructure. the hashcode is used for identity one or more than one items.            // here are some requirements to the hashcode function:            //1. if two item is equal, the hashkey must be equal.            //2. if two item is equal, the hashkey should not equal, but not must.            //3. the GetHashCode function should fast.            //4. the Hashcode should not change.            HashSet<Employee> employees = new HashSet<Employee>();            Employee kate = new Employee { Name = "Kate Jones" };            employees.Add(kate);            kate.Name = "Kate Jones-Smith";            // it really shock me, I don‘t notice this before.            bool has = employees.Contains(kate); //returns false!        }    }}

  version 1:

  public override bool Equals(object obj)

     首先參數ojbect 會做一次裝箱,然後調用Equals 由於是虛函數,而實值型別沒有函數指標表,無法調用虛函數,必須轉為參考型別,才能調用,我用ILDasm的確可以看到做了Box.

Version 2:
當我們重載了Equals函數,可以避免調用虛函數,這樣可以避免兩次裝箱。

version 3:
讓比較更加完善,對== , != 進行重載。

version 4:
繼承自IEquatable, 在CLR generics 中會用到。需要進一步的驗證。

version 5:
對HashCode的設計做了一些建議。不要依賴於可變的東西,否則正如上面例子所示範的,潛在的不穩定性。

總結
1. 使用實值型別,當我們會建立大量的執行個體,比如10,000,000
2. override Equals, oeverload Equals, implement IEquatable<T> , overload ==, !=,
3. override GetHashCode
4. 使實值型別為不變


參考

<<Pro .Net Performance>>




 

C# 實值型別 與參考型別

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.