C# 結構體與類的區別
經常聽到有朋友在討論C#中的結構與類有什麼區別.正好這幾日閒來無事,自己總結一下,希望大家指點.
1. 首先是語法定義上的區別啦,這個就不用多說了.定義類使用關鍵字class 定義結構使用關鍵字struct.在語法上其實類和結構有著很多相似的地方.
定義類的語法
1 class Person 2 { 3 private string name; 4 private int age; 5 6 public void SayHi() 7 { 8 Console.WriteLine("Hello,My Name is "+this.name+",My Age is "+this.age); 9 } 10 }
定義結構的語法.
1 struct Rectangle 2 { 3 private int width; 4 private int height; 5 6 public int GetArea() 7 { 8 return this.width * height; 9 } 10 }
從語法上來看.它們的語法都大同小異,類裡面的成員幾乎都可以定義在結構體中,但是解構函式除外.這是為什麼呢?後面解答.
2. 雖然我們說它們的語法極其相似,但是它們在語法還是有幾點區別的.
a.在結構體中可以宣告欄位,但是宣告欄位的時候是不能給初始值的.所以當我們試圖這樣寫程式碼的時候,C#編譯器在將原始碼編譯成程式集的是會提示語法錯誤.
我們知道如果我們在類中宣告1個欄位的同時給這個欄位賦初始值,這樣是可以滴,就像下面這樣.
class Person { private string name ="jack"; }
但是如果像下面這樣確實不行滴.宣告完1個欄位,再為這個欄位賦值,就像下面這樣.
1 class Person 2 { 3 private string name; 4 name="jack"; 5 }
所以我們說,在類下面只能直接定義類的成員,只能定義. 比如定義成員欄位,屬性 方法 建構函式等等.上面那樣的程式碼name="jack"這樣的程式碼我們稱之為“執行程式碼”,意思就是說這些程式碼只有在被執行的時候才會有效果.而你試想一下,那麼這些程式碼什麼時候被執行呢? 建立類的物件的時候? 那還用得著建構函式嗎? 經常看到一些初學者在類的下面直接寫這樣程式碼.
但是又有人會說了.誒, 那麼為什麼在宣告類的欄位的時候可以賦值呢?賦值表示式也是1個執行程式碼啊?為什麼這樣就不報錯呢?給你看看下面的程式碼 你就會知道其中的真相了.
當我們使用C#編譯器將這段程式碼編譯為程式集的時候,看看微軟為我們生成的程式碼吧.
展開建構函式,看看這裡面有什麼蹊蹺吧!
是的,C#編譯器在編譯的時候,如果我們宣告欄位的時候為欄位賦值,那麼為欄位賦值的程式碼C#編譯器在編譯的時候會將賦值的程式碼放到建構函式中去,其實嚴格意義上來說,類的欄位也是不能有初始值的.只不過微軟在背後幫我們做了點事情,我們不知道而已.
所以,不管在類和結構中,執行程式碼一定要寫在方法中.不能直接寫在結構或者類的下面.因為當執行程式碼寫在方法中了,那麼這些執行程式碼的執行時機才可以確定,就是這個方法被呼叫的時候了.
從上面的內容,我們可以看出.其實從本質上來說,類和結構的欄位都是不能有初始值的.只不過微軟在語法上允許我們在定義類的欄位的時候為其賦值.但是背後微軟其實是把賦值的執行程式碼放到建構函式中去執行的. 而結構體微軟卻不幫我們這樣做.至於這其中是什麼原因.查了些資料,也看了園子裡其他博友的文章,感覺都不能說服我,但是自己也想不出1個確切的理由微軟為什麼要這樣做.那就先放著吧,希望參透其中原理的童鞋能指點.
b. 關於建構函式.
首先,關於隱式建構函式.我們知道,在1個類中如果我們沒有為類寫任意的建構函式,那麼C#編譯器在編譯的時候會自動的為這個類生成1個無引數的建構函式.我們將這個建構函式稱之為隱式建構函式 但是一旦我們為這個類寫了任意的1個建構函式的時候,這個隱式的建構函式就不會自動生成了.
在結構中,就不是這樣了,在結構中隱式的建構函式無論如何都存在.看看程式碼吧.
在下面的程式碼中 我們為結構體寫了1個帶引數的建構函式.如下.
我們使用new關鍵字來建立結構體物件,我們發現呼叫建構函式的時候,提示是有兩個建構函式的.多了1個無引數的建構函式.
那麼 我們再想,能不能手動的寫1個無引數的建構函式呢?我們懷著無比激動的心情,試一下.
結果是華麗麗的報錯了.所以我們得出結論. 隱式的無引數的建構函式在結構中無論如何都是存在的,所以程式設計師不能手動的為結構新增1個無引數的建構函式.
關於建構函式當然還不僅僅如此.我們知道在類的建構函式中我們可以寫一些任意的程式碼(前提是符合C#語法啦),在結構體的建構函式中雖然也可以寫任意的程式碼.但是C#語法規定在結構體的建構函式中,必須要為結構體的所有欄位賦值.看看下面的程式碼吧.
啊哦.....報錯了.....
我們也知道,在結構中還可以定義屬性,所以有童鞋就這樣寫啦.看下面程式碼.
這個錯誤,仍然提示我們在建構函式中沒有為所有的欄位賦值,這是很多童鞋遇到的問題,誒,不是要在建構函式中為所有的欄位賦值麼?我現在賦值了啊。為什麼還是提示沒有賦值呢? 我們在建構函式中為屬性賦值 而屬性又為欄位賦值,為什麼這樣就不行呢? 原因很簡單.因為語法要求我們為所有的欄位賦值,雖然這裡我們看得出來為屬性賦值其實屬性再把值賦值給欄位, 我們說屬性是對欄位的操作,但是一定是這樣的嗎?我們完全可以在屬性的set塊裡面什麼都不寫,如果什麼都不寫,那麼屬性還是在操作欄位嗎? 所以屬性不一定是在操作欄位的,在結構體的建構函式中我們為屬性賦值,不認為是在對欄位賦值,所以我們在建構函式中要直接為欄位賦值.
c.建立結構體物件的方式.
建立結構體物件可以不使用new關鍵字.直接宣告1個變數就可以.但是這樣的話,結構體物件中的欄位是沒有初始值的,所以在使用欄位之前必須要為這個欄位賦值.
原因很簡單.因為宣告的時候就不能給初始值,雖然建構函式中為物件的欄位賦值,但是此種方式建立結構體物件,沒有呼叫建構函式,所以必須要程式設計師在使用之前手動賦值。下面這樣就可以了.
另外1種建立結構體物件的方式和類一樣,使用new關鍵字來建立,與不使用new關鍵字建立不同的是,通過使用new關鍵字建立結構體物件後,這個結構體物件的欄位就已經有值了.原因不難理解,new關鍵字呼叫了建構函式,而結構體建構函式要求必須要為所有的欄位賦值.
所以,我們不難猜出.結構體的無引數的建構函式做了什麼事情,在無引數的建構函式中為所有的欄位賦值,值型別的欄位賦值0,給引用型別的欄位賦值null.
d. 結構體不能從另外1個結構或者類繼承,但是可以實現介面.特殊的是.雖然結構不能從別的類或者結構繼承,但是所有的結構都預設從ValueType類繼承,ValueType類再從Object類繼承.所以結構體物件仍然擁有超類Object的成員.看看下面的微軟生成的程式碼就知道了.
3. 它們之間最大的區別 是結構體是值型別 類是引用型別.
結構體是值型別,當其作為1個區域性變數的時候,變數是儲存在棧空間中的,其物件的欄位直接儲存在這個變數中的.就像下面這樣.
與引用型別的類不一樣,引用型別的變數中儲存的是物件在堆空間中的地址,所以當我們傳遞1個引用型別的變數的時候,其實傳遞的是變數的值(物件的地址) 傳遞完以後 對變數的修改會影響到另外1個變數指向的物件的值.
4. 最後 談一下什麼時候使用結構,什麼使用類.
我們知道,結構儲存在棧中,而棧有1個特點,就是空間較小,但是訪問速度較快,堆空間較大,但是訪問速度相對較慢.所以當我們描述1個輕量級物件的時候,可以將其定義為結構來提高效率.比如點,矩形,顏色,這些物件是輕量級的物件,因為描述他們,只需要少量的欄位。當描述1個重量級物件的時候,我們知道類的物件是儲存在堆空間中的,我們就將重量級物件定義為類. 他們都表示可以包含資料成員和函式成員的資料結構。與類不同的是,結構是值型別並且不需要堆分配。結構型別的變數直接包含結構的資料,而類型別的變數包含對資料的引用(該變數稱為物件)。 struct 型別適合表示如點、矩形和顏色這樣的輕量物件。儘管可能將一個點表示為類,但結構在某些方案中更有效。在一些情況下,結構的成本較低。例如,如果宣告一個含有 1000 個點物件的陣列,則將為引用每個物件分配附加的記憶體。所以結構適合表示1個輕量級物件.
基於另外1個理由我也會使用結構. 我們在變數傳值的時候,我就是希望傳遞物件的拷貝,而不是物件的引用地址,那麼這個時候也可以使用結構了.
以上只是個人總結,難免會有些地方有瑕疵,歡迎大家指正,謝謝.!
相關文章
- C++中結構體與類的區別C++結構體
- C#中類和結構的一個區別...C#
- 類與結構體結構體
- Swift 類與結構體Swift結構體
- Swift,結構體與類Swift結構體
- C#點滴 - 抽象類與介面區別C#抽象
- c#中結構體和類的比較C#結構體
- .net與C#的區別C#
- c#與java的區別C#Java
- C#虛基類繼承與介面的區別C#繼承
- c# 中的靜態類(satic class)和具體類的區別及用法C#
- C#結構體C#結構體
- C++ protected許可權成員以及類和結構體區別C++結構體
- C++ 結構體struct和共同體union的區別C++結構體Struct
- iOS Swift結構體與類的方法排程iOSSwift結構體
- date類與calendar的區別
- JQ操作類與JS操作類的區別JS
- C#陣列與集合的區別C#陣列
- C#中List與IList的區別C#
- 第五章——結構體與類(序)結構體
- 偽類與偽元素的區別
- C#中結構體的應用C#結構體
- 類中的結構體或列舉等型別的前置宣告結構體型別
- 第五章——結構體與類(實體和值)結構體
- C# 中 Struct 和 Class 的區別總結C#Struct
- golang類和結構體Golang結構體
- LNMP架構與LNAMP架構的區別LNMP架構
- bs架構與cs架構的區別架構
- 抽象類與介面的區別抽象
- Java中類與物件的關係與區別Java物件
- 重建模與重構的區別
- 偽元素和偽類的區別總結
- 介面、資料結構、資訊架構的區別資料結構架構
- get與post的區別總結
- 軟連結與硬連結的區別
- java字串“==”與“equals”的差異及與c#的區別Java字串C#
- C#中誰最快:結構還是類?
- 圖解計算機結構與體系分類!!圖解計算機