Unity3DC# 學習總結記錄一

文末有彩蛋~

最近學習Unity3D,把學習過程中遇到的知識點記錄下來。

C# 靜態,安全語言

C++ 靜態,不安全語言

C#語言是型別安全的,其本質是有關型別操作的一種規範,即不能將一種型別當作另一種型別,除非它們真的存在轉換關係。

C/C++語言允許做一些非常規的事情,所以與 C#相比其功能更加強大,不過同時在使用不恰當時也帶來了很多隱患。

例如,C/C++有可能會透過一些不合理的途徑,將一種型別的值當作另一種完全不同型別的值。這種不合理的途徑是由於 C/C++中有的程式碼會以錯誤的方式檢查值中的原始位元組並解釋它們。

透過終端編譯並執行上述程式碼,並將hello作為引數傳入,

這是由於編譯器將int當作32位的值,而char則是8位的值,同時文字使用UTF-8或者ASCII來表示。在本例中,C程式碼將char指標當作了一個int指標,因此只取其前4個位元組(32位),並且將它作為一個數字處理。

C#要求其所有型別全部從System。Object類派生。 無論是開發者自己定義的型別,還是C#所提供的型別。因此,下面的兩種定義型別的方式,其含義是完全相同的,程式碼如下。

定義了4個公共方法和2個受保護的方法(包括但不限於):

Equals:若兩個物件具有相同的值,則返回true,否則返回false。

GetHashCode:返回物件的值的雜湊碼。

ToString:預設返回型別的完整名稱,即 this。GetType()。FullName。但是此方法經常被重寫,最典型的例子就是int型等重寫該方法以返回其值的字串形式。

GetType:返回一個從 Type類派生的型別例項,以指出呼叫 GetType 方法的物件是什麼型別。常用於為反射提供與物件型別有關的元資料資訊。

MemberwiseClone。

Finalize:虛方法,在物件被標誌為應該被作為垃圾回收之後,但在記憶體真正被回收之前,會呼叫該方法。因此,如果需要在回收記憶體前執行清理工作的型別應該重寫該方法。

自動推導變數:

C++: auto

C#: var

值型別和引用型別

C#中大部分型別都是引用型別,但是在實際開發中,可以發現程式設計師使用最多的還是值型別。引用型別總是從託管堆分配,C#要求所有的物件都使用new運算子建立。

C#中運算子new:

計算所需記憶體空間,new運算子會計算目標型別和包括System。Object類在內的,其所有基類中定義的所有例項欄位所需要的位元組數。

除此之外,為了方便Mono執行時管理物件,還有一些額外的資訊需要託管堆為其分配空間,如型別物件指標和同步索引塊。

完成計算物件所需的空間後,就要為物件在託管堆上分配所需要的記憶體空間了。分配的所有位元組都設為0。

記憶體空間分配完,接下來需要初始化(在(1)中所提過的)物件的“型別物件指標”以及“同步塊索引”。

當前3個準備步驟全部完成後,最後就要呼叫型別的例項構造器了。

構造器中自動呼叫當前型別的基類構造器。每個型別的構造器都負責初始化該型別定義的例項欄位。最終一定會呼叫 System。Object 的構造器,而該構造器僅僅是返回,而沒有什麼邏輯操作。

最後會返回指向新建物件的一個引用。也就是說新建的變數是一個指向某型別物件的引用,而非物件本身。

由於並沒有一個和new運算子相對應的delete運算子存在,因而物件的回收工作主要由垃圾回收機制,也就是GC來處理。

那麼在 C#語言中,到底哪些型別可以歸為是引用型別,又有哪些型別應該被歸為值型別呢?

根據ECMA的 C#語言規範(ECMA-334規範)或者微軟官方的C#語言規範,任何被稱為“類”的型別都是引用型別。

並且通常使用以下3個關鍵字來宣告一個自己定義的引用型別。

Class

Interface

Delegate

當然,在C#中也有一些內建的引用型別。

Dynamic

Object

string

值型別包括:

數字型結構:常見的有System。Int32結構、System。Float結構、System。Decimal結構等。

布林型結構:常見的無非是System。Boolean結構。

使用者自定義的結構。

特別注意的是C#的結構體 是屬於值型別,而C++中的結構體可以認為就是類。

值型別例項有兩種表示方式,分別是未裝箱和已裝箱。而裝箱機制是指將值型別轉換成引用型別。  正是由於值型別不在託管堆中分配,不被垃圾回收,同時也無須透過指標來引用,因而值型別與引用型別存在很多區別。但是有很多情況需要獲取和操作對值型別例項的引用。因而裝箱機制為了應對這種情況,便應運而生。

值型別派生自System。ValueType(即struct隱含的基型別是System。ValueType),而System。ValueType同樣從System。Object派生而來,因此System。ValueType提供了和System。Object相同的方法,不過值得注意的是System。ValueType重寫了Equals方法和GetHashCode方法。

如果符合以下條件,作為開發者可以選擇使用值型別而不是引用型別:

型別的例項較小時。

型別的例項較大時,不會作為方法的實參進行傳遞,或作為方法的返回值返回。

由於值型別不能作為基型別來派生新的值型別或引用型別,因此目標型別中不能引入新的虛方法,以及所有的方法都不能是抽象的,最後所有方法都隱式密封。

裝箱和拆箱

有時我們需要使用一個引用,而不是一個值型別的值,就需要裝箱,裝箱的步驟如下:

(1)在託管堆中分配記憶體。 需要注意的是,由於是將值型別進行引用型別化,因而分配的記憶體空間除了值型別各個欄位所需的記憶體之外,還要加上託管堆所有物件都有的兩個額外成員(型別物件指標和同步索引塊)所需的記憶體。

(2)將值型別的欄位複製到新分配的堆記憶體中。

(3)返回物件地址,即物件的引用。值型別成了引用型別。

由引用型別轉為指型別,則經過獲取引用並複製:

(1)獲取已經裝箱的物件中各個欄位的地址,這個過程便是所說的拆箱。

(2)將已經裝箱的物件中各個欄位的值從託管堆上覆制到執行緒棧的新的值型別例項中。

一個在使用C#語言進行開發的過程中經常遇到的一個誤區,就是將拆箱當作了裝箱的逆過程。其實並不是這樣的,相比於裝箱,拆箱的代價要小得多。拆箱其實就是獲取引用的過程,獲取的這個引用指向了一個分配在託管堆上的物件中的值。需要注意的是,拆箱並不涉及複製的過程,所以將值從託管堆上的物件中複製到值型別例項中,是拆箱之後緊跟的一步複製過程,而非拆箱本身。

在拆箱時,一定要注意只能轉型為最初未裝箱的值型別。而拆箱需要顯式的指定要轉型的目標型別這一點,也與裝箱不同。許多編譯器都是隱式生成程式碼來裝箱物件的,因此作為開發者有時會因為不注意而忽略這一點,所以如果是一個關心程式效能的開發者,就一定要清楚自己的程式碼究竟是否會造成裝箱操作。

以上就是最近學習Unity3D的學習記錄啦!最後附上一張Unity3D, Mono, C#的關係。

Unity3DC# 學習總結記錄一

TAG: 型別C#裝箱system引用