.Net常見面試題整理(1)——值類型和引用類型


為了防止不提供原網址的轉載,特在這里加上原文鏈接:http://www.cnblogs.com/zhangkai2237/archive/2013/03/17/2964528.html
         前段時間准備換個工作,所以參加了幾場面試,面試的中主要考察的是C#相關知識的掌握以及實際項目中的經驗。項目經驗沒辦法整理,每個人都不一樣,但是考察的基礎知識我們可以歸納總結,對自己是個整理總結提高的過程。
        首先申明本人於2011年7月份畢業,目前工作約兩年時間,面試的職位一般都是中級.Net工程師的職位。由於水平有限,文章中如果有錯誤或不到位的地方還請看到的大神指出,我一定改正。
        
        好了,回到正題。類型一直是C#中最基本的問題,關於值類型和引用類型,我想每個C#程序員都知道“值類型保存在棧上,引用類型保存在堆上”。但是僅僅知道到這里是完全不夠的,我們需要理解C#中的類型,了解為什么要有值類型和引用類型以及他們的特征。
 
一、值類型和引用類型的概念
 
        值類型的實例是在線程棧上分配的(不能免俗的提起這句話),值類型的變量並沒有一個指向實例的指針,而是變量中已經包含了實例本身的字段。
        相應的引用類型的實例時在托管堆中分配的,返回的是一個指向實例對象的內存地址。
 
        比如我們一個值類型的變量 valType, 他包含一個int的字段a,其值為5,他在棧和堆中的示意圖為:
    現有一個引用類型的變量refType,他指向RefType類的一個實例,下圖為示意圖:
        另外我們都知道基元類型中除了string類型,其他的都是值類型,但是我們大部分人都沒有發現他們之間的區別。只要我們進入各種基元類型的定義中就可以發現:string類型是一個class,而其他的值類型都是struct。翻閱資料發現了微軟在定義值類型和引用類型的區別:
         引用類型包括類和接口,所有的以class和interface修飾的類型都是引用類型;而值類型包括結構和枚舉,所有的結構和枚舉都是值類型。繼續查找資料發現所有的結構都是抽象類型System.ValueType,所有的枚舉都是派生自System.Enum類型的,而System.Enum類型也繼承自System.ValueType類。所以我們可以得出 值類型都是繼承自System.ValueType的結論。
 
        值類型還有一個重要的特征是因為結構是隱式密封的,所以我們沒辦法由自值類型來派生一個我們想要的類型來。例如我們無法從System.Int32(int)類派生出另外一個類型來。
 
二、為什么要有值類型
        FCL中的絕大多數類型都是引用類型,那么為什么要有值類型呢?首先我們回顧下實例化一個引用類型的步驟:
            1. 計算好在托管堆上分配的內存大小,包括該類型實例的所有字段的大小,以及在托管堆中的兩個“額外成員”(《CLR via C#》中的翻譯):類型對象指針和同步塊索引;
            2. 在托管堆中分配指定大小的內存塊;
            3. 初始化對象的“類型對象指針”和“同步塊索引”;
            4、調用類型的構造函數,如果調用的是有參構造函數,則還要向其傳入參數。注意,如果要實例化子類對象,則必須先調用父類的構造函數,一直會追溯到System.Object。這部分具體的可以參看《C#入門經典》相關章節。
 
        如果所有的類型都是引用類型,那么我們程序的性能將顯著下降。引用類型在性能方面還有一個重大的問題是垃圾回收。當沒有變量指向某個對象時,那么該對象就會成為垃圾,就會成為GC回收的對象,而GC是相當耗費資源的。而使用值類型是不需要垃圾回收的,對象超過了其作用域就會自動銷毀。
 
        上面這段解釋了為什么我們需要值類型,講解了值類型在性能上的又是,那么我們為何還需要引用類型呢?
        
        我們都知道值類型的傳值是要復制整個對象的,而引用類型僅僅是復制指向實例對象的指針。而且值類型不能被繼承,所以值類型適合一些比較輕量和簡單的類型,否則同樣會出現性能問題。
        那么我們應該什么時候使用值類型呢:
必須滿足以下所有條件,否則不要定義成值類型
第一,類型具有基元類型的行為。類型簡單,其中沒有成員會修改類型的任何實例字段。
第二,類型不需要從其他任何類型繼承。
第三,類型不會派生出其他任何類型。
除了滿足以上全部條件,還必須滿足以下條件中的一個。
第一,類型的實例較小(約是16字節或者更小)。
第二,類型實例較大,但不作為方法的實參傳遞,也不通過方法返回。(這樣即使很大但是不需實參傳遞,不會進行復制的操作)
 
        到這里我們基本上把值類型和引用類型的一些基本知識了解完了,那么這部分面試官喜歡問什么問題呢?
             值類型和引用類型的區別
                1. 值類型分配在內存棧上,引用類型分配在托管堆上。當一個值類型的變量賦給另一個值類型的變量時,會執行一次逐字段的復制,而一個引用類型的變量賦給另一個引用類型的變量時,僅僅會復制對象的內存地址。
                2. 基於上一條,多個引用類型的變量可以同時指向同一個對象,對其中的任何一個變量執行操作都會影響到另一個變量引用的對象。而每個值類型的變量都已經包含了自己的對象,所以對值類型對象的操作不會影響到另一個值類型變量。
                3. 值類型包括結構和枚舉,他們均間接或直接派生自System.ValueType類;引用類型包括類和接口,他們都派生自System.Object類(這一句是廢話,所有的類型都派生自System.Object類,可說可不說)。
                4. 值類型都是隱式密封的,不能將一個值類型作為基類來定義一個新的值類型或者引用類型,也因此值類型中不能包括虛方法(不能被繼承,虛方法給誰重寫呢)。
                5、默認情況下,創建一個引用類型的變量時,他會被初始化為null;而創建一個值類型時,他的所有成員都會被初始化為0.
                6. 值類型的變量一旦超過了其作用域,為他分配的內存就會被立即釋放;而引用類型則會在托管堆里待一段時間,直到垃圾回收器將其回收。
                7. 由於System.ValueType類重寫了Equals方法,所以兩個值類型的Equals方法會在兩個對象的字段完全匹配的情況下返回true;而引用類型的Equals則會在兩個變量引用同一個對象的情況下才返回true。(這一條不重要,不說也無所謂,但是如果被問到自己要有所了解)。
 
             數組是值類型還是引用類型?
                我第一次遇到的這個問題的時候並沒有特別注意,但是心想他既然問這樣的問題,應該是引用類型,所以堅定的回答”引用類型“,讓面試官看不出來我是猜的。所以童鞋們以后如果遇到類似的問題,即使是猜的也要理直氣壯,否則即使你答對了,面試官也知道你是猜的,在這個問題上還是會被扣分的。
                數組類型的確是引用類型,可能有部分童鞋不承認,那我們簡單的寫一個驗證方法:
            
       #region 數組的類型
            int[] intArray = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            int[] copyArray = intArray;
            intArray[0] = 9;
            Console.WriteLine(copyArray[0]);
            #endregion

                輸出為:9.

 
             結構和類的區別
                實際上就是值類型和引用類型的區別,對照着第一題回答就行了。
 
        到這里,值類型和引用類型還有一個特別重要的點沒有提到,那就是裝箱和拆箱。實際上是一個很簡答的問題,但是如果你不了解,在面試時會很難回答。並且在日常的工作中很有可能經常掉入裝箱的陷阱,對程序的性能有較大的影響。所以我打算下一篇就將裝箱和拆箱。
 
        好了,第一篇寫完了,希望大家給給建議,我希望和大家共同努力成長。
        


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM