結構與類區別

2021-03-04 06:50:30 字數 3697 閱讀 6489

經常聽到有朋友在討論c#中的結構與類有什麼區別.正好這幾日閒來無事,自己總結一下,希望大家指點.

1. 首先是語法定義上的區別啦,這個就不用多說了.定義類使用關鍵字class 定義結構使用關鍵字struct.在語法上其實類和結構有著很多相似的地方.

定義類的語法

1class person

210 }

定義結構的語法.

1struct rectangle

210 }

從語法上來看.它們的語法都大同小異,類裡面的成員幾乎都可以定義在結構體中,但是析構函式除外.這是為什麼呢?後面解答.

2. 雖然我們說它們的語法極其相似,但是它們在語法還是有幾點區別的.

a.在結構體中可以宣告字段,但是宣告欄位的時候是不能給初始值的.所以當我們試圖這樣寫**的時候,c#編譯器在將源**編譯成程式集的是會提示語法錯誤.

我們知道如果我們在類中宣告1個字段的同時給這個欄位賦初始值,這樣是可以滴,就像下面這樣.

class person

但是如果像下面這樣確實不行滴.宣告完1個字段,再為這個字段賦值,就像下面這樣.

1class person

2 所以我們說,在類下面只能直接定義類的成員,只能定義. 比如定義成員字段,屬性方法建構函式等等.上面那樣的**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個理由我也會使用結構. 我們在變數傳值的時候,我就是希望傳遞物件的拷貝,而不是物件的引用位址,那麼這個時候也可以使用結構了.

以上只是個人總結,難免會有些地方有瑕疵,歡迎大家指正,謝謝.!

抽象類與介面的區別

當需要滿足d條件時,只能使用抽象類,否則也可以考慮使用介面實現。3 什麼時候使用介面?當滿足以下的條件時,最好使用介面進行設計 a 子類已經繼承了其它父類 b 子類中不存在完全相同的功能實現方法 c 子類中不存在相同的屬性 d 設計出的結構不需要繼承其它類 當需要滿足a條件時,只能使用介面,否則也可...

三大類岩石的區別

沉積岩 是地面即成岩石在外力作用下,經過風化 搬運 沉積固結等沉積而成,其主要特徵是 層理構造顯著 沉積岩中常含化石 有的具有乾裂 孔隙 結核等。常見的沉積岩有 直徑大於3公釐的礫和磨圓的卵石及被其它物質膠結而形成的礫岩,由2公釐到0.05公釐直徑的砂粒膠結而成的砂岩,由顆粒細小的粘土礦物組成的頁岩...

怎樣區別被動語態與係表結構

怎樣區別被動語態與 連系動詞be 過去分詞 的結構 be 過去分詞 並不一定都是被動語態,有時是係表結構。當 be 過去分詞 表示動作時為被動語態,be是助動詞,be後面的過去分詞是主要動詞,動作的物件是主語 當 be 過去分詞 表示主語所處的狀態時為係表結構,be是連繫動詞。be後面的過去分詞是表...