[你必须知道的.NET] 第五回:深入浅出关键字---把new说透(转载)
[你必須知道的.NET] 第五回:深入淺出關(guān)鍵字---把new說透
?作者:Anytao
?
?本文將介紹以下內(nèi)容:
- 面向?qū)ο蠡靖拍?
- new關(guān)鍵字深入淺出
- 對象創(chuàng)建的內(nèi)存管理?
1.?引言
園子里好像沒有或者很少把new關(guān)鍵字拿出來說的,那我就占個先機吧,呵呵。那么,我們到底有必要將一個關(guān)鍵字拿出來長篇大論嗎?看來是個問題。回答的關(guān)鍵是:你真的理解了new嗎?如果是,那請不要浪費時間,如果不是,那請繼續(xù)本文的循序之旅。
下面幾個 問題可以大概的考察你對new的掌握,開篇之前,希望大家做個檢驗,如果通過了,直接關(guān)掉本頁即可。如果沒有通過,希望本文的闡述能幫你找出答案。
2. 基本概念
一般說來,new關(guān)鍵字在.NET中用于以下幾個場合,這是MSDN的典型解釋:
- 作為運算符, 用于創(chuàng)建對象和調(diào)用構(gòu)造函數(shù)。
本文的重點內(nèi)容,本文在下一節(jié)來重點考慮。
- 作為修飾符,用于向基類成員隱藏繼承成員。
作為修飾符,基本的規(guī)則可以總結(jié)為:實現(xiàn)派生類中隱藏方法,則基類方法必須定義為virtual;new作為修飾符,實現(xiàn)隱藏基類成員時,不可和override共存,原因是這兩者語義相斥:new用于實現(xiàn)創(chuàng)建一個新成員,同時隱藏基類的同名成員;而override用于實現(xiàn)對基類成員的擴展。
另外,如果在子類中隱藏了基類的數(shù)據(jù)成員,那么對基類原數(shù)據(jù)成員的訪問,可以通過base修飾符來完成。
例如:?
new作為修飾符using?System;
namespace?Anytao.net.My_Must_net
{
????class?Number
????{
????????public?static?int?i?=?123;
????????public?void?ShowInfo()
????????{
????????????Console.WriteLine("base?class---");
????????}
????????public?virtual?void?ShowNumber()
????????{
????????????Console.WriteLine(i.ToString());
????????}
????}
????class?IntNumber?:?Number
????{
????????new?public?static?int?i?=?456;
????????public?new?virtual?void?ShowInfo()
????????{
????????????Console.WriteLine("Derived?class---");
????????}
????????public?override?void?ShowNumber()
????????{
????????????Console.WriteLine("Base?number?is?{0}",?Number.i.ToString());
????????????Console.WriteLine("New?number?is?{0}",?i.ToString());????????????
????????}
????}
????class?Tester
????{
????????public?static?void?Main(string[]?args)
????????{
????????????Number?num?=?new?Number();
????????????num.ShowNumber();
????????????IntNumber?intNum?=?new?IntNumber();
????????????intNum.ShowNumber();
????????????Number?number?=?new?IntNumber();
????????????//究竟調(diào)用了誰?
????????????number.ShowInfo();
????????????//究竟調(diào)用了誰?
????????????number.ShowNumber();
????????}
????}
}
- 作為約束,用于在泛型聲明中約束可能用作類型參數(shù)的參數(shù)的類型。
MSDN中的定義是:new 約束指定泛型類聲明中的任何類型參數(shù)都必須有公共的無參數(shù)構(gòu)造函數(shù)。當泛型類創(chuàng)建類型的新實例時,將此約束應用于類型參數(shù)。
注意:new作為約束和其他約束共存時,必須在最后指定。
其定義方式為:
class?Genericer<T>?where?T?:?new()????{
????????public?T?GetItem()
????????{
????????????return?new?T();
????????}
????}
實現(xiàn)方式為:
class?MyCls????{
????????private?string?_name;
????????public?MyCls()
????????{
????????????_name?=?"Emma";
????????}
????}
????{
????????public?static?void?Main(string[]?args)
????????{
????????????Genericer<MyCls>?MyGen?=?new?Genericer<MyCls>();
????????????Console.WriteLine(MyGen.GetItem().Name);
????????}
????}
- 使用new實現(xiàn)多態(tài)。 這不是我熟悉的話題,詳細的內(nèi)容可以參見?《多態(tài)與 new [C#]》,這里有較詳細的論述。
3. 深入淺出
作為修飾符和約束的情況,不是很難理解的話題,正如我們看到本文開篇提出的問題,也大多集中在new作為運算符的情況,因此我們研究的重點就是揭開new作為運算符的前世今生。
Jeffrey Richter在其著作中,極力推薦讀者使用ILDASM工具查看IL語言細節(jié),從而提高對.NET的深入探究,在我認為這真是一條不錯的建議,也給了自己很多提高的空間挖掘。因此,以下是本人的一點建議,我將在后續(xù)的系列中,關(guān)于學習方法論的討論中深入探討,這里只是順便小議,希望有益于大家。
1 不斷的學習代碼;
2 經(jīng)常看看IL語言的運行細節(jié),對于提供.NET的認識非常有效。
文歸正題,new運算符用于返回一個引用,指向系統(tǒng)分配的托管堆的內(nèi)存地址。因此,在此我們以Reflector工具,來了解以下new操作符執(zhí)行的背后,隱藏著什么玄機。
首先我們實現(xiàn)一段最簡單的代碼,然后分析其元數(shù)據(jù)的實現(xiàn)細節(jié),來探求new在創(chuàng)建對象時到做了什么??
new作為運算符using?System;
namespace?Anytao.net.My_Must_net
{
????class?MyClass
????{
????????private?int?_id;
????????public?MyClass(int?id)
????????{
????????????_id?=?id;
????????}
????}
????struct?MyStruct
????{
????????private?string?_name;
????????public?MyStruct(string?name)
????????{
????????????_name?=?name;
????????}
????}
????class?NewReflecting
????{
????????public?static?void?Main(string[]?args)
????????{
????????????int?i;
????????????int?j?=?new?int();
????????????MyClass?mClass?=?new?MyClass(123);
????????????MyStruct?mStruct?=?new?MyStruct("My?Struct");
????????}
????}
}
使用Reflector工具反編譯產(chǎn)生的IL代碼如下為:?
IL元數(shù)據(jù)分析.method?public?hidebysig?static?void?Main(string[]?args)?cil?managed
{
????.entrypoint
????.maxstack?2
????.locals?init?(
????????[0]?int32?num,
????????[1]?int32?num2,
????????[2]?class?Anytao.net.My_Must_net._05_new.MyClass?class2,
????????[3]?valuetype?Anytao.net.My_Must_net._05_new.MyStruct?struct2)
????L_0000:?nop?
????
????//初始化j為0
????L_0001:?ldc.i4.0?
????L_0002:?stloc.1?
????
????//使用newobj指令創(chuàng)建新的對象,并調(diào)用構(gòu)造函數(shù)以0x76(123的16進制)初始化
????L_0003:?ldc.i4.s?0x7b????
????L_0005:?newobj?instance?void?Anytao.net.My_Must_net._05_new.MyClass::.ctor(int32)
????L_000a:?stloc.2?
????//加載“My?Struct”
????L_000b:?ldloca.s?struct2
????L_000d:?ldstr?"My?Struct"
????//調(diào)用構(gòu)造函數(shù)執(zhí)行初始化
????L_0012:?call?instance?void?Anytao.net.My_Must_net._05_new.MyStruct::.ctor(string)
????L_0017:?nop?
????L_0018:?ret?
}
從而可以得出以下結(jié)論:
- new一個class時,new完成了以下兩個方面的內(nèi)容:一是調(diào)用newobj命令來為實例在托管堆中分配內(nèi)存;二是調(diào)用構(gòu)造函數(shù)來實現(xiàn)對象初始化。
- new一個struct時,new運算符用于調(diào)用其帶構(gòu)造函數(shù),完成實例的初始化。
- new一個int時,new運算符用于初始化其值為0。
- 另外必須清楚,值類型和引用類型在分配內(nèi)存時是不同的,值類型分配于線程的堆棧(stack)上,并變量本身就保存其實值,因此也不受GC的控制,;而引用類型變量,包含了指向托管堆的引用,內(nèi)存分配于托管堆(managed heap)上,內(nèi)存收集由GC完成。?
另外還有以下規(guī)則要多加注意:
- new運算符不可重載。
- new分配內(nèi)存失敗,將引發(fā)OutOfMemoryException異常。?
對于基本類型來說,使用new操作符來進行初始化的好處是,某些構(gòu)造函數(shù)可以完成更優(yōu)越的初始化操作,而避免了不高明的選擇,例如:
string?str?=?new?string('*',?100);string?str?=?new?string(new?char[]?{'a',?'b',?'c'});
而不是
string?str?=?"***************************************";?
4. 結(jié)論
??? 我能說的就這么多了,至于透了沒透,作者的能量也就這么多了。希望園子的大牛們常來扔塊磚頭,對我也是一種莫大的促進。但是作為基本的原理和應用,我想對大部分的需求是滿足了。希望這種力求深入淺出的介紹,能給你分享new關(guān)鍵字和其本質(zhì)的來龍去脈能有所幫助。?
言歸正傳,開篇的幾個題目,不知讀者是否有了各自的答案,我們不妨暢所欲言,做更深入的討論,以便揭開其真實的面紗。?
參考文獻
(USA)Stanley B.Lippman, C# Primer
(USA)David Chappell Understanding .NET
?
廣而告之
[預告]
另外鑒于前幾個主題的討論中,不管是類型、關(guān)鍵字等都涉及到引用類型和值類型的話題,我將于近期發(fā)表相關(guān)內(nèi)容的探討,同時還有其他的關(guān)鍵字值得研究,這是本系列近期動向,給自己做個廣告。祝各位愉快。?
[聲明]?
本文的關(guān)鍵字new指的是C#中的關(guān)鍵字概念,并非一般意義上的.NET CRL范疇,之所以將這個主題加入本系列,是基于在.NET體系下開發(fā)的我們,何言能逃得過基本語言的只是要點。所以大可不必追究什么是.NET,什么是C#的話題,希望大家理清概念,有的放肆。
總結(jié)
以上是生活随笔為你收集整理的[你必须知道的.NET] 第五回:深入浅出关键字---把new说透(转载)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我的跳槽经验
- 下一篇: java ioutils 写入文件_文件