轉(zhuǎn)載至
Unity面試題:《Unity面試寶典》八股文 - 全網(wǎng)最新最全!
C#中值類型和引用類型
在C#中,值類型和引用類型是兩種基本的變量類型,它們具有不同的特性和使用場景。同時,繼承是面向?qū)ο缶幊讨械囊粋€核心概念,它允許創(chuàng)建一個類(子類)來繼承另一個類(父類)的屬性和方法。下面我將詳細(xì)解釋C#中的值類型和引用類型,以及它們與繼承的關(guān)系。
值類型(Value Types)
值類型變量直接包含其數(shù)據(jù)值。當(dāng)創(chuàng)建一個值類型變量時,會直接在棧內(nèi)存中為其分配空間,并且變量的值直接存儲在這個內(nèi)存位置中。值類型包括簡單的數(shù)據(jù)類型(如int、double、bool、char等)、結(jié)構(gòu)體(struct)、枚舉(enum)以及值類型的可空類型(如int?)。
值類型的特點:
直接包含數(shù)據(jù):值類型的變量直接存儲其數(shù)據(jù)值,而不是指向數(shù)據(jù)的引用。
棧內(nèi)存分配:值類型變量在棧內(nèi)存中分配空間,它們的生命周期與包含它們的方法或代碼塊的執(zhí)行時間相關(guān)。
數(shù)據(jù)復(fù)制:當(dāng)值類型變量被賦值給另一個變量時,會創(chuàng)建該值的一個副本。兩個變量將具有相同的值,但存儲在不同的內(nèi)存位置。
不可變性:大多數(shù)值類型是不可變的,一旦賦值就不能更改(例如,int、double等)。但是,結(jié)構(gòu)體(struct)和枚舉(enum)可以包含可變字段。
自動內(nèi)存管理:值類型的內(nèi)存管理由C#運行時自動處理。當(dāng)值類型變量超出其作用域時,其內(nèi)存會自動釋放。
引用類型(Reference Types)
引用類型變量不直接包含其數(shù)據(jù)值,而是存儲對其數(shù)據(jù)值在堆內(nèi)存中的位置的引用。引用類型包括類(class)、接口(interface)、數(shù)組和委托(delegate)。
引用類型的特點:
存儲引用:引用類型變量存儲的是一個引用(或指針),指向在堆內(nèi)存中存儲的實際對象。
堆內(nèi)存分配:實際對象在堆內(nèi)存中分配空間,而引用類型變量在棧內(nèi)存中分配空間,僅存儲對堆上對象的引用。
數(shù)據(jù)復(fù)制:當(dāng)引用類型變量被賦值給另一個變量時,復(fù)制的是引用本身,而不是對象本身。這意味著兩個變量現(xiàn)在指向同一個對象。
可變性:引用類型通常是可變的,可以通過修改其屬性或字段來改變其狀態(tài)。
垃圾回收:引用類型的內(nèi)存管理由C#的垃圾回收器(Garbage Collector)負(fù)責(zé)。當(dāng)對象不再被任何引用類型變量引用時,垃圾回收器會釋放其占用的內(nèi)存。
繼承情況
繼承是面向?qū)ο缶幊痰囊粋€重要概念,它允許一個類(子類)繼承另一個類(父類)的屬性和方法。在C#中,通過使用:符號來實現(xiàn)繼承關(guān)系。子類可以繼承父類的公有和保護成員(包括字段、屬性、方法和事件),并可以添加自己的新成員或重寫父類的虛擬或抽象成員。
繼承的特點:
代碼重用:子類可以重用父類的代碼,避免了重復(fù)編寫相同的代碼。
擴展性:子類可以在繼承父類的基礎(chǔ)上添加新的功能或修改現(xiàn)有功能,從而實現(xiàn)對父類的擴展。
多態(tài)性:通過繼承,子類可以重寫父類的方法,從而實現(xiàn)多態(tài)性。這意味著可以根據(jù)對象的實際類型來調(diào)用不同的方法實現(xiàn)。
繼承層次結(jié)構(gòu):C#支持多層繼承,即一個類可以繼承自另一個類,后者又可以繼承自另一個類,依此類推,形成繼承層次結(jié)構(gòu)。
需要注意的是,值類型不能直接繼承自引用類型,反之亦然。引用類型繼承自引用類型(例如,類可以繼承自另一個類或?qū)崿F(xiàn)一個或多個接口)。
注意,結(jié)構(gòu)體不能繼承自類。因為結(jié)構(gòu)體是值類型,而類是引用類型,但是可以實現(xiàn)接口。
例外: C++中,結(jié)構(gòu)體是可以被繼承的。
總之,值類型和引用類型是C#中兩種基本的變量類型,它們在內(nèi)存管理、數(shù)據(jù)復(fù)制和可變性等方面有著顯著的區(qū)別。
而繼承則是面向?qū)ο缶幊讨械囊粋€核心概念,允許通過繼承來創(chuàng)建更復(fù)雜、更具擴展性的類層次結(jié)構(gòu)。
簡述值類型和引用類型有何區(qū)別?
值類型的數(shù)據(jù)存儲在內(nèi)存的棧中;引用類型的數(shù)據(jù)存儲在內(nèi)存的堆中,而內(nèi)存單元中只存放堆中對象的地址。
值類型存取速度快,引用類型存取速度慢。
值類型表示實際數(shù)據(jù),引用類型表示指向存儲在內(nèi)存堆中的數(shù)據(jù)的指針或引用。
值類型繼承自System.ValueType,引用類型繼承自System.Object。
棧的內(nèi)存分配是自動釋放;而堆在.NET中會有GC來釋放。
值類型的變量直接存放實際的數(shù)據(jù),而引用類型的變量存放的則是數(shù)據(jù)的地址,即對象的引用。
值類型變量直接把變量的值保存在堆棧中,引用類型的變量把實際數(shù)據(jù)的地址保存在堆棧中。
什么是裝箱、拆箱,怎樣減少操作
在C#中,裝箱(Boxing)和拆箱(Unboxing)是兩種與值類型和引用類型之間的轉(zhuǎn)換相關(guān)的操作。
裝箱(Boxing)
裝箱是將值類型轉(zhuǎn)換為引用類型的過程。具體來說,它是將值類型的實例包裝到一個對象中,這個對象在堆上分配,并包含該值類型實例的一個副本。裝箱操作通常發(fā)生在以下情況:
將值類型賦值給引用類型的變量時。
將值類型作為參數(shù)傳遞給接受引用類型參數(shù)的方法時。
在值類型上調(diào)用需要引用類型接收者的方法時(例如,調(diào)用繼承自 System.Object 的方法)。
裝箱操作會引入額外的內(nèi)存分配和復(fù)制成本,因為它需要在堆上創(chuàng)建一個新的對象來存儲值類型的值。
拆箱(Unboxing)
拆箱是從對象中提取值類型值的過程。它是裝箱的逆操作,即從引用類型到值類型的轉(zhuǎn)換。拆箱操作要求執(zhí)行時的對象確實包含所需的值類型,否則將拋出 InvalidCastException 異常。
拆箱操作同樣有成本,因為它需要檢查對象的實際類型,并從對象中復(fù)制值到值類型的變量中。
如何減少裝箱和拆箱操作
裝箱和拆箱操作會引入性能開銷,特別是在性能關(guān)鍵的代碼中。為了減少這些操作,可以采取以下策略:
避免不必要的裝箱:盡量使用值類型而不是引用類型,除非有明確的理由需要使用引用類型。避免在不需要的情況下對值類型進行裝箱。
使用泛型:泛型允許你編寫靈活且類型安全的代碼,同時避免裝箱和拆箱操作。例如,使用泛型集合(如 List<T>)而不是非泛型的 ArrayList。
避免在值類型上調(diào)用虛擬方法:值類型上的虛擬方法調(diào)用會導(dǎo)致裝箱,因為虛擬方法是通過對象的方法表來調(diào)用的。
顯式拆箱:在需要拆箱的情況下,盡量顯式地進行拆箱,而不是依賴于隱式拆箱和類型轉(zhuǎn)換。這可以提高代碼的可讀性和維護性。
代碼示例
下面是一個簡單的示例,演示了裝箱和拆箱操作:
int value = 42; // 值類型變量?
object obj = value; // 隱式裝箱:將int值包裝到object類型的引用中?
Console.WriteLine(obj.GetType()); // 輸出: System.Int32,證明obj引用了一個int類型的對象?
int unboxedValue = (int)obj; // 顯式拆箱:從object類型的引用中提取int值?
Console.WriteLine(unboxedValue); // 輸出: 42,拆箱成功,獲取了原始的值42
在這個示例中,value 是一個 int 類型的值變量。當(dāng)我們將它賦值給 object 類型的變量 obj 時,發(fā)生了隱式裝箱操作。然后,我們通過顯式拆箱將 obj 轉(zhuǎn)換回 int 類型,并將結(jié)果存儲在 unboxedValue 中。
然而,在實際編程中,應(yīng)該盡量避免不必要的裝箱和拆箱操作,以提高程序的性能。使用泛型集合和避免在值類型上調(diào)用虛擬方法是減少裝箱和拆箱的常見做法。
Unity面試寶典(八股文) https://docs.qq.com/doc/DRmdXVFFySkhsRHph