C#中struct和class的差別詳解。本站提示廣大學習愛好者:(C#中struct和class的差別詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是C#中struct和class的差別詳解正文
本文具體剖析了C#中struct和class的差別,關於C#初學者來講是有需要加以懂得並控制的。
簡略來講,struct是值類型,創立一個struct類型的實例被分派在棧上。class是援用類型,創立一個class類型實例被分派在托管堆上。但struct和class的差別遠不止這麼簡略。
歸納綜合來說,struct和class的分歧表現在:
● 類是援用類型,struct是值類型
● 在托管堆上創立類的實例,在棧上創立struct實例
● 類實例的賦值,賦的是援用地址,struct實例的賦值,賦的是值
● 類作為參數類型傳遞,傳遞的是援用地址,struct作為參數類型傳遞,傳遞的是值
● 類沒有默許無參結構函數,struct有默許無參結構函數
● 類支撐繼續,struct不支撐繼續
● 類傾向於"面向對象",用於龐雜、年夜型數據,struct傾向於"簡略值",好比小於16字節,構造簡略
● 類的成員很輕易賦初值,很難給struct類型成員賦初值
● 類的實例只能經由過程new SomeClass()來創立,struct類型的實例既可以經由過程new SomeStruct()來創立,也能夠經由過程SomeStruct myStruct;來創立
1、從賦值的角度體驗struct和class的分歧
援用類型賦值,是把地址賦值給了變量
class Program { static void Main(string[] args) { SizeClass sizeClass = new SizeClass(){Width = 10, Length = 10}; Console.WriteLine("賦值前:width={0},length={1}", sizeClass.Width, sizeClass.Length); var copyOfSizeClass = sizeClass; copyOfSizeClass.Length = 5; copyOfSizeClass.Width = 5; Console.WriteLine("賦值後:width={0},length={1}",sizeClass.Width, sizeClass.Length); Console.ReadKey(); } } public class SizeClass { public int Width { get; set; } public int Length { get; set; } } public struct SizeStruct { public int Width { get; set; } public int Length { get; set; } }
運轉成果以下圖所示:
以上,當把sizeClass賦值給copyOfSize變量的時刻,是把sizeClass所指向的地址賦值給了copyOfSize變量,2個變量同時指向統一個地址。所以,當轉變copyOfSizeClass變量的值,也相當於轉變了sizeClass的值。
struct類型賦值,是完整拷貝,在棧上多了一個完整一樣的變量
class Program { static void Main(string[] args) { SizeStruct sizeStruct = new SizeStruct(){Length = 10, Width = 10}; Console.WriteLine("賦值前:width={0},length={1}", sizeStruct.Width, sizeStruct.Length); var copyOfSizeStruct = sizeStruct; copyOfSizeStruct.Length = 5; copyOfSizeStruct.Width = 5; Console.WriteLine("賦值後:width={0},length={1}", sizeStruct.Width, sizeStruct.Length); Console.ReadKey(); } }
法式運轉成果以下圖所示:
以上,當把sizeStruct賦值給copyOfSizeStruct變量的時刻,是完整拷貝,轉變copyOfSizeStruct的值不會影響到sizeStruct。
2、從參數傳值角度體驗struct和class的分歧
援用類型參數傳遞的是地址
class Program { static void Main(string[] args) { List<string> temp = new List<string>(){"my","god"}; temp.ForEach(t => Console.Write(t + " ")); Console.ReadKey(); } public static void ChangeReferenceType(List<string> list) { list = new List<string>(){"hello", "world"}; } }
運轉成果:my god
為何不是hello world?
→棧上的temp指向托管堆上的一個聚集實例
→當temp放到ChangeReferenceType(temp)辦法中,實質是把temp指向的地址賦值給了變量list
→在ChangeReferenceType(List<string> list)辦法外部,又把變量list的指向了別的一個聚集實例地址
→但temp的指向地址一向沒有轉變
我們再來轉變ChangeReferenceType(List<string> list)外部完成方法,其它不變。
class Program { static void Main(string[] args) { List<string> temp = new List<string>(){"my","god"}; ChangeReferenceType(temp); temp.ForEach(t => Console.Write(t + " ")); Console.ReadKey(); } public static void ChangeReferenceType(List<string> list) { list.Clear(); list.Add("hello"); list.Add("world"); } }
運轉成果:hello world
為何不是my god?
→棧上的temp指向托管堆上的一個聚集實例
→當temp放到ChangeReferenceType(temp)辦法中,實質是把temp指向的地址賦值給了變量list
→在ChangeReferenceType(List<string> list)辦法外部,把temp和list配合指向的實例清空,又添加"hello"和"world"2個元素
→因為list和temp指向的實例是一樣的,所以轉變list指向的實例就同等於轉變temp指向的實例
以上,很好地解釋了:援用類型參數傳遞的是地址。
值類型struct參數傳遞的是值
class Program { static void Main(string[] args) { Size s = new Size(){Length = 10, Width = 10}; ChangeStructType(s); Console.Write("Length={0},Width={1}", s.Length,s.Width); Console.ReadKey(); } public static void ChangeStructType(Size size) { size.Length = 0; size.Width = 0; } } public struct Size { public int Length { get; set; } public int Width { get; set; } }
運轉成果以下圖所示:
為何Length和Width不是0呢?
→在棧上變量size
→當經由過程ChangeStructType(size),把s變量賦值給ChangeStructType(Size size)中的size變量,其實質是在棧上又創立了一個變量size,size的值和s是完整一樣的
→在ChangeStructType(Size size)外部轉變size的值,與變量s毫有關系
3、從struct類型的struct類型屬性和struct援用類型屬性體驗struct和class的分歧
假定有一個struct,它有struct類型的屬性
以下, struct類型Room有struct類型的屬性TableSize和TvSize,我們若何經由過程Room實例來修正其struct類型的屬性值呢?
class Program { static void Main(string[] args) { Room r = new Room() { TableSize = new Size(){Length = 100, Width = 80}, TvSize = new Size(){Length = 10, Width = 8} }; r.TableSize.Length = 0; Console.WriteLine("table今朝的尺寸是:length={0},width={1}", r.TableSize.Length, r.TableSize.Width); Console.ReadKey(); } } public struct Size { public int Length { get; set; } public int Width { get; set; } } public struct Room { public Size TableSize { get; set; } public Size TvSize { get; set; } }
以上,r.TableSize.Length = 0;此處會報錯:不克不及修正r.TableSize的值,由於不是變量。切實其實,r.TableSize只是Size的一份拷貝,並且也沒有賦值給其它變量,所以r.TableSize是暫時的,會被主動收受接管,對其賦值也是沒成心義的。
假如要修正r.TableSize,只需把
r.TableSize.Length = 0;
改成以下:
r.TableSize = new Size(){Length = 0, Width = 0};
運轉成果以下圖所示:
可見,轉變struct類型的struct類型屬性的某個屬性是行欠亨的,由於像以上r.TableSize只是一份拷貝,是暫時的,會被主動收受接管的。要轉變struct類型的struct類型屬性,就須要像下面一樣,給r.TableSize賦上一個完全的Size實例。
假定有一個struct,它有援用類型的屬性呢?
以下,struct類型的Room有援用類型屬性,TableSize和TvSize,若何經由過程Room實例來修正其援用類型的屬性值呢?而且,我們在類Size中界說了一個事宜,當給Size的屬性賦值時就觸發事宜,提醒size類的屬性值產生了轉變。
class Program { static void Main(string[] args) { var oneSize = new Size() {Length = 10, Width = 10}; var twoSize = oneSize; oneSize.Changed += (s, e) => Console.Write("Size產生了轉變~~"); oneSize.Length = 0; Console.ReadKey(); } } public class Size { private int _length; private int _width; public event System.EventHandler Changed; public int Length { get { return _length; } set { _length = value; OnChanged(); } } public int Width { get { return _width; } set { _width = value; OnChanged(); } } private void OnChanged() { if (Changed != null) { Changed(this, new EventArgs()); } } } public struct Room { public Size TableSize { get; set; } public Size TvSize { get; set; } }
運轉,顯示:Size產生了轉變~~
對oneSize.Length的修正,現實上修正的是oneSize.Length指向托管堆上的實例。
4、從結構函數體驗struct和class的分歧
struct類型包括隱式的默許無參結構函數
class Program { static void Main(string[] args) { var size = new SizeStruct(); Console.WriteLine("length={0},width={1}", size.Length, size.Width); Console.ReadKey(); } } public struct SizeStruct { public int Length { get; set; } public int Width { get; set; } }
運轉成果以下圖所示:
為何我們沒有給SizeStruct界說無參結構函數,而沒有報錯?
--由於,struct類型有一個隱式的無參結構函數,而且給一切的成員賦上默許值,int類型屬性成員的默許值是0。
類不包括隱式無參結構函數
class Program { static void Main(string[] args) { var size = new SizeClass(); Console.WriteLine("length={0},width={1}", size.Length, size.Width); Console.ReadKey(); } } public class SizeClass { public int Length { get; set; } public int Width { get; set; } public SizeClass(int length, int width) { Length = length; Width = Width; } }
運轉,報錯:SizeClass不包括0個參數的結構函數
5、從給類型成員賦初值體驗struct和class的分歧
假如直接給字段賦初值。
public struct SizeStruct { public int _length = 10; }
運轉,報錯:構造中不克不及有實例字段初始值設定項
假如經由過程結構函數給字段賦初值。
public struct SizeStruct { public int _length; public SizeStruct() { _length = 10; } }
運轉,報錯:構造中不克不及包括顯式無參數結構函數
可見,給struct類型成員賦初值是不太輕易的,而給class成員賦初值,no problem。
什麼時候應用struct,什麼時候應用class?
在多半情形下,推舉應用class類,由於不管是類的賦值、作為參數類型傳遞,照樣前往類的實例,現實拷貝的是托管堆上援用地址,也就年夜概4個字節,這異常有助於機能的晉升。
而作為struct類型,不管是賦值,作為參數類型傳遞,照樣前往struct類型實例,是完整拷貝,會占用棧上的空間。依據Microsoft's Value Type Recommendations,在以下情形下,推舉應用struct:
● 小於16個字節
● 傾向於值,是簡略數據,而不是傾向於"面向對象"
● 願望值弗成變