[你必須知道的.NET] 第五回:深入淺出關鍵字---把new說透

本文將介紹以下內容:

  • 面向對象基本概念
  • new關鍵字深入淺出
  • 對象創建的內存管理
  • 1.引言

    園子里好像沒有或者很少把new關鍵字拿出來說的,那我就佔個先機吧,呵呵。那麼,我們到底有必要將一個關鍵字拿出來長篇大論嗎?看來是個問題。回答的關鍵是:你真的理解了new嗎?如果是,那請不要浪費時間,如果不是,那請繼續本文的循序之旅。

    下面幾個 問題可以大概的考察你對new的掌握,開篇之前,希望大家做個檢驗,如果通過了,直接關掉本頁即可。如果沒有通過,希望本文的闡述能幫你找出答案。

    1. new一個class對象和new一個struct或者enum有什麼不同?
    2. new在.NET中有幾個用途,除了創建對象實例,還能做什麼?
    3. new運算符,可以重載嗎?
    4. 范型中,new有什麼作用?
    5. new一個繼承下來的方法和override一個繼承方法有何區別?
    6. int i和int i = new int()有什麼不同?

    2. 基本概念

    一般說來,new關鍵字在.NET中用於以下幾個場合,這是MSDN的典型解釋:

  • 作為運算符, 用於創建對象和調用構造函數。
  • 本文的重點內容,本文在下一節來重點考慮。

  • 作為修飾符,用於向基類成員隱藏繼承成員。
  • 作為修飾符,基本的規則可以總結為:實現派生類中隱藏方法,則基類方法必須定義為virtual;new作為修飾符,實現隱藏基類成員時,不可和override共存,原因是這兩者語義相斥:new用於實現創建一個新成員,同時隱藏基類的同名成員;而override用於實現對基類成員的擴展。

    另外,如果在子類中隱藏了基類的數據成員,那麼對基類原數據成員的訪問,可以通過base修飾符來完成。

    例如:

    new作為修飾符usingSystem;namespaceAnytao.net.My_Must_net{classNumber{publicstaticinti=123;publicvoidShowInfo(){Console.WriteLine("baseclass---");}publicvirtualvoidShowNumber(){Console.WriteLine(i.ToString());}}classIntNumber:Number{newpublicstaticinti=456;publicnewvirtualvoidShowInfo(){Console.WriteLine("Derivedclass---");}publicoverridevoidShowNumber(){Console.WriteLine("Basenumberis{0}",Number.i.ToString());Console.WriteLine("Newnumberis{0}",i.ToString());}}classTester{publicstaticvoidMain(string[]args){Numbernum=newNumber();num.ShowNumber();IntNumberintNum=newIntNumber();intNum.ShowNumber();Numbernumber=newIntNumber();//究竟調用了誰?number.ShowInfo();//究竟調用了誰?number.ShowNumber();}}}

  • 作為約束,用於在泛型聲明中約束可能用作類型參數的參數的類型。
  • MSDN中的定義是:new 約束指定泛型類聲明中的任何類型參數都必須有公共的無參數構造函數。當泛型類創建類型的新實例時,將此約束應用於類型參數。

    注意:new作為約束和其他約束共存時,必須在最後指定。

    其定義方式為:

    classGenericer<T>whereT:new(){publicTGetItem(){returnnewT();}}

    實現方式為:

    classMyCls{privatestring_name;publicMyCls(){_name="Emma";}}classMyGenericTester{publicstaticvoidMain(string[]args){Genericer<MyCls>MyGen=newGenericer<MyCls>();Console.WriteLine(MyGen.GetItem().Name);}}

  • 使用new實現多態。 這不是我熟悉的話題,詳細的內容可以參見《多態與 new [C#]》,這裡有較詳細的論述。
  • 3. 深入淺出

    作為修飾符和約束的情況,不是很難理解的話題,正如我們看到本文開篇提出的問題,也大多集中在new作為運算符的情況,因此我們研究的重點就是揭開new作為運算符的前世今生。

    Jeffrey Richter在其著作中,極力推薦讀者使用ILDASM工具查看IL語言細節,從而提高對.NET的深入探究,在我認為這真是一條不錯的建議,也給了自己很多提高的空間挖掘。因此,以下是本人的一點建議,我將在後續的系列中,關於學習方法論的討論中深入探討,這裡只是順便小議,希望有益於大家。1 不斷的學習代碼;2 經常看看IL語言的運行細節,對於提供.NET的認識非常有效。

    文歸正題,new運算符用於返回一個引用,指向系統分配的託管堆的內存地址。因此,在此我們以Reflector工具,來了解以下new操作符執行的背後,隱藏著什麼玄機。

    首先我們實現一段最簡單的代碼,然後分析其元數據的實現細節,來探求new在創建對象時到做了什麼?

    new作為運算符usingSystem;namespaceAnytao.net.My_Must_net{classMyClass{privateint_id;publicMyClass(intid){_id=id;}}structMyStruct{privatestring_name;publicMyStruct(stringname){_name=name;}}classNewReflecting{publicstaticvoidMain(string[]args){inti;intj=newint();MyClassmClass=newMyClass(123);MyStructmStruct=newMyStruct("MyStruct");}}}

    使用Reflector工具反編譯產生的IL代碼如下為:

    IL元數據分析.methodpublichidebysigstaticvoidMain(string[]args)cilmanaged{.entrypoint.maxstack2.localsinit([0]int32num,[1]int32num2,[2]classAnytao.net.My_Must_net._05_new.MyClassclass2,[3]valuetypeAnytao.net.My_Must_net._05_new.MyStructstruct2)L_0000:nop//初始化j為0L_0001:ldc.i4.0L_0002:stloc.1//使用newobj指令創建新的對象,並調用構造函數以0x76(123的16進位)初始化L_0003:ldc.i4.s0x7bL_0005:newobjinstancevoidAnytao.net.My_Must_net._05_new.MyClass::.ctor(int32)L_000a:stloc.2//載入「MyStruct」L_000b:ldloca.sstruct2L_000d:ldstr"MyStruct"//調用構造函數執行初始化L_0012:callinstancevoidAnytao.net.My_Must_net._05_new.MyStruct::.ctor(string)L_0017:nopL_0018:ret}

    從而可以得出以下結論:

  • new一個class時,new完成了以下兩個方面的內容:一是調用newobj命令來為實例在託管堆中分配內存;二是調用構造函數來實現對象初始化。
  • new一個struct時,new運算符用於調用其帶構造函數,完成實例的初始化。
  • new一個int時,new運算符用於初始化其值為0。
  • 另外必須清楚,值類型和引用類型在分配內存時是不同的,值類型分配於線程的堆棧(stack)上,並變數本身就保存其實值,因此也不受GC的控制,;而引用類型變數,包含了指向託管堆的引用,內存分配於託管堆(managed heap)上,內存收集由GC完成。
  • 另外還有以下規則要多加註意:

  • new運算符不可重載。
  • new分配內存失敗,將引發OutOfMemoryException異常。
  • 對於基本類型來說,使用new操作符來進行初始化的好處是,某些構造函數可以完成更優越的初始化操作,而避免了不高明的選擇,例如:

    stringstr=newstring(『*『,100);stringstr=newstring(newchar[]{『a『,『b『,『c『});

    而不是

    stringstr="***************************************";

    4. 結論

    我能說的就這麼多了,至於透了沒透,作者的能量也就這麼多了。希望園子的大牛們常來扔塊磚頭,對我也是一種莫大的促進。但是作為基本的原理和應用,我想對大部分的需求是滿足了。希望這種力求深入淺出的介紹,能給你分享new關鍵字和其本質的來龍去脈能有所幫助。

    言歸正傳,開篇的幾個題目,不知讀者是否有了各自的答案,我們不妨暢所欲言,做更深入的討論,以便揭開其真實的面紗。

    參考文獻

    (USA)Stanley B.Lippman, C# Primer

    (USA)David Chappell Understanding .NET

    廣而告之

    [預告]

    另外鑒於前幾個主題的討論中,不管是類型、關鍵字等都涉及到引用類型和值類型的話題,我將於近期發表相關內容的探討,同時還有其他的關鍵字值得研究,這是本系列近期動向,給自己做個廣告。祝各位愉快。

    [聲明]

    本文的關鍵字new指的是C#中的關鍵字概念,並非一般意義上的.NET CRL範疇,之所以將這個主題加入本系列,是基於在.NET體系下開發的我們,何言能逃得過基本語言的只是要點。所以大可不必追究什麼是.NET,什麼是C#的話題,希望大家理清概念,有的放肆。


    推薦閱讀:

    C#網路編程技術微軟Socket實戰項目演練(三)
    .net core下驗證碼及二維碼登錄的實現
    debian(kali Linux) 安裝net Core
    .net core下對於Excel的一些操作及使用
    C#多線程技術提高RabbitMQ消費吞吐率(二)

    TAG:.NET | 知道 | 第五回 |