C#類(lèi)和結(jié)構(gòu)詳解
類(lèi)和結(jié)構(gòu)實(shí)際上都是創(chuàng)建對(duì)象(實(shí)例)的模版,每個(gè)對(duì)象都包含數(shù)據(jù),并提供了處理和訪問(wèn)數(shù)據(jù)的方法。
類(lèi)定義了類(lèi)的每個(gè)對(duì)象可以包含什么數(shù)據(jù)和功能。
class PhoneCus { public const string DaySend = "Mon"; public int CusId; }
結(jié)構(gòu)與類(lèi)的區(qū)別是它們?cè)趦?nèi)存中的存儲(chǔ)方式,訪問(wèn)方式和它們的一些特性(稍后詳細(xì)介紹它們的區(qū)別)。
較小的數(shù)據(jù)類(lèi)型使用結(jié)構(gòu)可提高性能,在語(yǔ)法上,比較類(lèi)似,主要區(qū)別是使用關(guān)鍵字struct代替class來(lái)聲明結(jié)構(gòu)。
struct PhoneCusStruct { public const string DaySend = "Mon"; public int CusId=; }
對(duì)于類(lèi)和結(jié)構(gòu),都是用new來(lái)聲明實(shí)例:這個(gè)關(guān)鍵字創(chuàng)建對(duì)象并對(duì)其進(jìn)行初始化。
PhoneCus myCus = new PhoneCus(); PhoneCusStruct myCus2 = new PhoneCusStruct();
上面的例子,類(lèi)和結(jié)構(gòu)的字段值都默認(rèn)0.
一.類(lèi)
類(lèi)中的數(shù)據(jù)和函數(shù)稱(chēng)為類(lèi)的成員(數(shù)據(jù)成員和函數(shù)成員)。
1.數(shù)據(jù)成員
數(shù)據(jù)成員是包含類(lèi)的數(shù)據(jù)————字段,常量和事件的成員。數(shù)據(jù)成員可以是靜態(tài)數(shù)據(jù)。類(lèi)成員總是實(shí)例成員,除非用static顯示聲明。
2.函數(shù)成員
函數(shù)成員提供了操作類(lèi)中數(shù)據(jù)的某些功能,包括方法,屬性,構(gòu)造函數(shù),終結(jié)器,運(yùn)算符以及索引。
(1)方法
*C#區(qū)分函數(shù)和方法。C#中函數(shù)包含上述提到的。
*給方法傳遞參數(shù)
參數(shù)可以通過(guò)引用或值傳遞給方法。在變量通過(guò)引用傳遞給方法時(shí),被調(diào)用的方法得到的就是這個(gè)變量,準(zhǔn)確的説就是指向內(nèi)存中變量的指針。所以在方法內(nèi)對(duì)變量進(jìn)行的任何改變?cè)诜椒ㄍ顺龊笕匀挥行А?/p>
而如果變量通過(guò)值傳遞給方法,被調(diào)用的方法得到的是變量的一個(gè)相同副本,也就是說(shuō),在方法退出后,對(duì)變量的修改會(huì)丟失。
對(duì)于復(fù)雜的數(shù)據(jù)類(lèi)型,按引用傳遞的效率更高,因?yàn)樵诎粗祩鬟f時(shí),必須復(fù)制大量的數(shù)據(jù)。
注意字符串的行為方式有所不同,因?yàn)樽址遣豢勺兊?,所以字符串無(wú)法采用一般引用類(lèi)型的行為方式。在方法調(diào)用中,對(duì)字符串所做的改變都不會(huì)影響原始字符串。
*ref參數(shù)
像上面所説,值類(lèi)型通過(guò)值傳遞變量是默認(rèn)的。但也可以迫使值參數(shù)通過(guò)引用傳遞給方法。為此要使用ref關(guān)鍵字。這樣該方法對(duì)變量所做的任何改變都會(huì)影響原始值。
static void SomeFunction(int[] ints,ref int i) { ints[0] = 100; i = 100; }
在調(diào)用該方法的時(shí)候,必須添加ref關(guān)鍵字。
SomeFunction(ints, ref i);
*out參數(shù)
C#要求變量在被引用前必須用一個(gè)初始值進(jìn)行初始化。但使用out關(guān)鍵字來(lái)初始化可以簡(jiǎn)化C# 編譯器所堅(jiān)持的輸入?yún)?shù)的初始化。
在方法的輸入?yún)?shù)前加上out前綴時(shí),傳遞給該方法的變量可以不初始化。而且該變量通過(guò)引用傳遞,所以在從被調(diào)用的方法中返回時(shí),對(duì)應(yīng)方法對(duì)該變量進(jìn)行的任何改變都會(huì)保留下來(lái)。
在調(diào)用該方法時(shí),仍需要使用out關(guān)鍵字:
static void SomeFunction(int[] ints,out int i) { ints[0] = 100; i = 100; } SomeFunction(ints, out i);
*命名參數(shù)
參數(shù)一般需要按定義的順序傳遞給方法。命名參數(shù)允許按任意順序傳遞。
string FullName(string firstName,string lastName) { renturn firstName+" " +lastName; }
調(diào)用方法:
FullName("John","Doe"); FullName(lastName:"Doe",firstName:"John");
*可選參數(shù)
參數(shù)也可以是可選的。必須為可選參數(shù)提供默認(rèn)值??蛇x參數(shù)還必須是方法定義的最后一個(gè)參數(shù)。
void TestMethod(int notOption,int option = 10) { Console.WriteLine( notOption + option); }
*方法的重載
C#支持方法的重載————方法的幾個(gè)版本有不同的簽名(方法名相同,但參數(shù)的個(gè)數(shù)和/或類(lèi)型不同)。
class MathTest { public int Value; public int GetSquare() { return Value*Value; } public int GetSquare(int x) { return x*x; } }
重載方法在參數(shù)方面的一些限制:
兩個(gè)方法不能僅在返回類(lèi)型上有區(qū)別;
兩個(gè)方法不能僅根據(jù)參數(shù)是聲明為ref還是out來(lái)區(qū)分。
在任何語(yǔ)言中,對(duì)于方法重載,如果調(diào)用了錯(cuò)誤的重載方法,就有可能出現(xiàn)運(yùn)行錯(cuò)誤。(后面討論如何避免這些錯(cuò)誤)。
(2)屬性(property)
屬性是一個(gè)方法或一對(duì)方法,在客戶(hù)端看來(lái),它是一個(gè)字段。
public string SomeProperty { get { return "value"; } set { //設(shè)置屬性值 } }
get訪問(wèn)器不帶任何參數(shù),且必須返回屬性聲明的類(lèi)型。也不應(yīng)為set訪問(wèn)器指定任何顯示參數(shù),編譯器會(huì)
假定它帶一個(gè)參數(shù),器類(lèi)型也許屬性相同,并表示為value.
private int age public int Age { get { return age; } set { age = valeu; } }
注意所用的命名約定,采用C#的區(qū)分大小寫(xiě)模式,使用相同的名稱(chēng),但公有屬性采用大寫(xiě)形式命名,如果存在一個(gè)等價(jià)的私有字段,則采用小寫(xiě)形式命名。
一些開(kāi)發(fā)人員喜歡使用把下劃線作為前綴的字段名,如_age,這會(huì)為識(shí)別字段提供極大的便利。
*只讀和只寫(xiě)屬性
在屬性定義中省略set訪問(wèn)器,就會(huì)創(chuàng)建只讀屬性。這樣客戶(hù)端代碼只可以讀取該屬性的值,但不能設(shè)置值。
private int age public int Age { get { return age; } }
同樣在屬性定義中省略get訪問(wèn)器,就會(huì)創(chuàng)建只寫(xiě)屬性。
*屬性的訪問(wèn)修飾符
C#允許給屬性的gei和set訪問(wèn)器設(shè)置不同的訪問(wèn)修飾符,所以屬性可以有公有的get訪問(wèn)器和受保護(hù)的set訪問(wèn)器。
在gey和set訪問(wèn)器中,必須有一個(gè)具有屬性的訪問(wèn)級(jí)別(公有)。
*自動(dòng)實(shí)現(xiàn)的屬性
如果屬性的set和get訪問(wèn)器中沒(méi)有任何邏輯,就可以使用自動(dòng)實(shí)現(xiàn)的屬性。這種屬性會(huì)自動(dòng)實(shí)現(xiàn)后背成員變量。
public int Age { get; set; }
不需要聲明private int age;,編譯器會(huì)自動(dòng)創(chuàng)建它。
使用自動(dòng)實(shí)現(xiàn)的屬性,就不能在屬性設(shè)置中驗(yàn)證屬性的有效性。但必須有兩個(gè)訪問(wèn)器,不能把屬性設(shè)置為只讀或只寫(xiě)。
public int Age { get;//報(bào)錯(cuò) } 但是,每個(gè)訪問(wèn)器的訪問(wèn)級(jí)別可以不同, public int Age { get; private set; }
(3)構(gòu)造函數(shù)
聲明基本構(gòu)造函數(shù)就是聲明一個(gè)與包含的類(lèi)同名的方法,但該方法沒(méi)有返回值。
public class MyClass { public MyClass() { } // }
一般情況下,如果沒(méi)有提供任何構(gòu)造函數(shù),編譯器會(huì)在后臺(tái)創(chuàng)建一個(gè)默認(rèn)的構(gòu)造函數(shù)。這是一個(gè)基本的構(gòu)造函數(shù),它只能把所有的成員字段初始化為標(biāo)準(zhǔn)的默認(rèn)值。這通常就足夠了,否則需要編寫(xiě)自己的構(gòu)造函數(shù)。
構(gòu)造函數(shù)的重載與其它方法的規(guī)則相同??梢詾闃?gòu)造函數(shù)提供任意多的的重載,只要它們的簽名有明顯區(qū)別。
public class MyClass { public MyClass() { } public MyClass(int i ) { / / } // }
如果提供了帶參數(shù)的構(gòu)造函數(shù),編譯器就不會(huì)自動(dòng)提供默認(rèn)的構(gòu)造函數(shù)。只有在沒(méi)有定義任何構(gòu)造函數(shù)的時(shí)候,編譯器才會(huì)自動(dòng)提供默認(rèn)的構(gòu)造函數(shù)。
public class MyNum { private int number; public MyNum(int number) { this.number =number; } }
一般使用this關(guān)鍵字區(qū)分成員字段和同名的參數(shù)。
如果試圖使用無(wú)參數(shù)的構(gòu)造函數(shù)實(shí)例化對(duì)象就會(huì)報(bào)錯(cuò):
MyNum num = new MyNum();//報(bào)錯(cuò)
可以把構(gòu)造函數(shù)定義為private或protected,這樣不相關(guān)的類(lèi)就不能訪問(wèn)它們:
public class MyNum { private int number; private MyNum(int number) { this.number =number; } }
上述例子沒(méi)有為MyNum定義為任何公有或受保護(hù)的構(gòu)造函數(shù)。這就使MyNum不能使用new運(yùn)算符在外部代碼中實(shí)例化,但可以在MyNum類(lèi)中編寫(xiě)一個(gè)公有靜態(tài)屬性或方法,以實(shí)例化該類(lèi)。
這在下面兩種情況下受有用的:
類(lèi)僅用作某些靜態(tài)成員或?qū)傩缘娜萜?,因此永遠(yuǎn)不會(huì)實(shí)例化它。
希望類(lèi)僅通過(guò)某個(gè)靜態(tài)成員函數(shù)來(lái)實(shí)例化。
*靜態(tài)構(gòu)造函數(shù)
C#可以給類(lèi)編寫(xiě)無(wú)參數(shù)的靜態(tài)構(gòu)造函數(shù)。這種構(gòu)造函數(shù)只執(zhí)行一次,而前面的構(gòu)造函數(shù)是實(shí)例構(gòu)造函數(shù),只要?jiǎng)?chuàng)建類(lèi)的對(duì)象,就會(huì)
執(zhí)行它。
class MyClass { static MyClass() { } }
編寫(xiě)靜態(tài)構(gòu)造函數(shù)的一個(gè)原因是,類(lèi)有一些靜態(tài)字段或?qū)傩裕枰诘谝淮问褂妙?lèi)之前,從外部源中初始化這些靜態(tài)字段和屬性。
.NET運(yùn)行庫(kù)不能確保什么時(shí)候執(zhí)行靜態(tài)構(gòu)造函數(shù),所以不能把要求在某個(gè)特定時(shí)刻執(zhí)行的代碼放在靜態(tài)構(gòu)造函數(shù)中。也不能預(yù)計(jì)不同類(lèi)的靜態(tài)構(gòu)造函數(shù)按照什么順序執(zhí)行。但是可以確保靜態(tài)構(gòu)造函數(shù)最多運(yùn)行一次,就在代碼引用類(lèi)之前調(diào)用它。
在C#中,通常在第一次調(diào)用類(lèi)的任何成員之前執(zhí)行靜態(tài)構(gòu)造函數(shù)。
注意,靜態(tài)構(gòu)造函數(shù)沒(méi)有訪問(wèn)修飾符,其它C#代碼從來(lái)不調(diào)用它,但在加載類(lèi)時(shí),總是由.NET運(yùn)行庫(kù)調(diào)用它,所以像public,private這樣的訪問(wèn)修飾符就沒(méi)有任何意義。出于同樣原因,靜態(tài)構(gòu)造函數(shù)不能帶任何參數(shù),一個(gè)類(lèi)也只能有一個(gè)靜態(tài)構(gòu)造函數(shù)。很顯然,靜態(tài)構(gòu)造只能訪問(wèn)累的靜態(tài)成員,不能訪問(wèn)類(lèi)的實(shí)例成員。
無(wú)參數(shù)的實(shí)例構(gòu)造函數(shù)與靜態(tài)構(gòu)造函數(shù)可以在同一個(gè)類(lèi)中同時(shí)定義。雖然參數(shù)列表相同,但這并不矛盾,因?yàn)樵诩虞d類(lèi)的時(shí)候執(zhí)行靜態(tài)構(gòu)造函數(shù),在創(chuàng)建實(shí)例時(shí)執(zhí)行實(shí)例構(gòu)造函數(shù),所以何時(shí)執(zhí)行哪個(gè)構(gòu)造函數(shù)不會(huì)有沖突。
如果任何靜態(tài)字段有默認(rèn)值,就在調(diào)用靜態(tài)構(gòu)造函數(shù)之前指定它們。
下面演示靜態(tài)構(gòu)造函數(shù)的用法:
class MainEntryPoint { static void Main() { Console.WriteLine("UserPreference:BackColor is " + UserPreference.BackColor.ToString()); } } class UserPreference { public static readonly Color BackColor; static UserPreference() { BackColor = Color.Red; } private UserPreference() { } }
該靜態(tài)變量在靜態(tài)構(gòu)造函數(shù)中進(jìn)行初始化。
*從構(gòu)造函數(shù)中調(diào)用其它構(gòu)造函數(shù)
有時(shí),在一個(gè)類(lèi)中有幾個(gè)構(gòu)造函數(shù),這些構(gòu)造函數(shù)包含一些共同的代碼。
class Car { private string des; private int nWheels; public Car(string des,int nWheels) { this.des = des; this.nWheels = nWheels; } public Car(string des) { this.des = des; this.nWheels = 4; } }
這兩個(gè)構(gòu)造函數(shù)初始化了相同的字段,顯然最好把所有的代碼放在一個(gè)地方。C#有一個(gè)特殊的語(yǔ)法,稱(chēng)為構(gòu)造函數(shù)初始化器,可以實(shí)現(xiàn)這個(gè)目的。
class Car { private string des; private int nWheels; public Car(string des,int nWheels) { this.des = des; this.nWheels = nWheels; } public Car(string des):this(des,4) { } }
這里,this關(guān)鍵字僅調(diào)用參數(shù)最匹配的那個(gè)構(gòu)造函數(shù)。構(gòu)造函數(shù)初始化器在構(gòu)造函數(shù)的函數(shù)體之前執(zhí)行。
C#構(gòu)造函數(shù)初始化器可以包含對(duì)同一個(gè)類(lèi)的另一個(gè)構(gòu)造函數(shù)的調(diào)用,也可以包含對(duì)直接基類(lèi)的構(gòu)造函數(shù)的調(diào)用,使用同樣的語(yǔ)法,但應(yīng)用base關(guān)鍵字代替this.初始化器中不能有多個(gè)調(diào)用。
3.只讀字段
常量是一個(gè)包含不能修改的值的變量。但常量不必滿(mǎn)足所有的要求。有時(shí)需要一些一些變量,其值不應(yīng)改變,但在運(yùn)行之前其值是未知的。C#為這種情形提供了另一種類(lèi)型的變量:只讀字段(readonly)。
readonly關(guān)鍵字比const靈活得多,允許把一個(gè)字段設(shè)置為常量,但可以執(zhí)行一些計(jì)算,以確定它得初始值。
其規(guī)則是可以在構(gòu)造函數(shù)中給只讀字段賦值,但不能在其它地方賦值。只讀字段還可以是一個(gè)實(shí)例字段,類(lèi)的每個(gè)實(shí)例可以有不同得值。
與const不同,如果要把只讀字段設(shè)置為靜態(tài),就必須顯示得聲明它。
二.匿名類(lèi)型
var關(guān)鍵字用于表示隱式類(lèi)型化得變量。var和new關(guān)鍵字一起使用時(shí),可以創(chuàng)建匿名類(lèi)型。
匿名類(lèi)型只是一個(gè)繼承自O(shè)bject且沒(méi)有名稱(chēng)的類(lèi)。
var caption = new {FirstName = "John",LastName="Doe"};
這會(huì)生成一個(gè)包含F(xiàn)irstName,LastName屬性的對(duì)象。
創(chuàng)建另一個(gè)對(duì)象:
var doctor = new {FirstName = "James",LastName="Mc"};
caption和doctor的類(lèi)型就相同,可以設(shè)置caption = doctor
如果設(shè)置的值來(lái)自于另一個(gè)對(duì)象,就可以簡(jiǎn)化初始化器。
var doctor = new {caption.FirstName,caption.LastName};
這些對(duì)象的類(lèi)型名未知。編譯器為類(lèi)型“偽造”了一個(gè)名稱(chēng),但只有編譯器才能使用它。
三.結(jié)構(gòu)(struct)
如果僅需要一個(gè)小的數(shù)據(jù)結(jié)構(gòu),此時(shí)類(lèi)提供的功能多余我們需要的功能,由于性能原因,最好使用結(jié)構(gòu)。
結(jié)構(gòu)是值類(lèi)型,它們存儲(chǔ)在棧中或存儲(chǔ)為內(nèi)聯(lián)(inline)(如果它們是存儲(chǔ)在堆中的另一個(gè)對(duì)象的一部分),其生存期的限制與簡(jiǎn)單的數(shù)據(jù)類(lèi)型一樣。
- *結(jié)構(gòu)不支持繼承。
- *對(duì)于結(jié)構(gòu),構(gòu)造函數(shù)的方式與類(lèi)有一些區(qū)別。編譯器總是提供一個(gè)無(wú)參數(shù)的默認(rèn)構(gòu)造函數(shù),它是不允許替換的。
- *使用結(jié)構(gòu)可以指定字段如何在內(nèi)存中布局(后面詳細(xì)介紹)
結(jié)構(gòu)實(shí)際上是把數(shù)據(jù)項(xiàng)組合在一起,有時(shí)大多數(shù)字段都聲明為public。嚴(yán)格來(lái)說(shuō),這與編寫(xiě).net代碼的規(guī)則相反(字段應(yīng)總是私有的(除const字段外),并由公有屬性封裝)。但是,對(duì)于簡(jiǎn)單的結(jié)構(gòu),公有字段是可以接受的編程方式。
四.類(lèi)和結(jié)構(gòu)的區(qū)別
1.結(jié)構(gòu)是值類(lèi)型
雖然結(jié)構(gòu)是值類(lèi)型,但在語(yǔ)法上可以把它當(dāng)作類(lèi)來(lái)處理。
struct PhoneCusStruct { public const string DaySend = "Mon"; public int CusId=0; } PhoneCusStruct phoneCusStruct = new PhoneCusStruct(); phoneCusStruct.CusId=3;
因?yàn)榻Y(jié)構(gòu)是值類(lèi)型,所以new運(yùn)算符與類(lèi)和其它引用類(lèi)型的工作方式不同。new運(yùn)算符并不分配堆中的內(nèi)存,而只是調(diào)用相應(yīng)的構(gòu)造函數(shù),根據(jù)傳送給它的參數(shù),初始化所有的字段。
對(duì)于結(jié)構(gòu)編寫(xiě)下面的代碼是合法的:
PhoneCusStruct phoneCusStruct; phoneCusStruct.CusId=3;
結(jié)構(gòu)遵循其它數(shù)據(jù)類(lèi)型都遵循的規(guī)則:在使用前所有的元素都必須進(jìn)行初始化。在結(jié)構(gòu)上調(diào)用new運(yùn)算符,或者給所有的字段分別賦值,結(jié)構(gòu)就完全初始化了。
如果結(jié)構(gòu)定義為類(lèi)的成員字段,在初始化包含的對(duì)象時(shí),該結(jié)構(gòu)會(huì)自動(dòng)初始化為0.
結(jié)構(gòu)是會(huì)影響性能的值類(lèi)型,但根據(jù)使用結(jié)構(gòu)的方式,這種影響可能是正面的,也可能是負(fù)面的。正面的影響是為結(jié)構(gòu)分配內(nèi)存時(shí),速度很快,因?yàn)樗鼈儗?nèi)聯(lián)或保存在棧中。在結(jié)構(gòu)超出了作用域被刪除時(shí),速度也很快,不需要等待垃圾回收。負(fù)面影響是,只要把結(jié)構(gòu)作為參數(shù)來(lái)傳遞或者把一個(gè)結(jié)構(gòu)賦予另一個(gè)結(jié)構(gòu),結(jié)構(gòu)的內(nèi)容就會(huì)被復(fù)制,而對(duì)于類(lèi)只復(fù)制引用。這樣就會(huì)有性能損失,根據(jù)結(jié)構(gòu)的大小,性能損失也不同。
注意,結(jié)構(gòu)主要用于小的數(shù)據(jù)結(jié)構(gòu)。當(dāng)把結(jié)構(gòu)作為參數(shù)傳遞給方法時(shí),應(yīng)把它作為ref參數(shù)傳遞,以避免性能損失(這樣只傳遞了結(jié)構(gòu)在內(nèi)存中的地址)。
2.結(jié)構(gòu)和繼承
結(jié)構(gòu)不能從一個(gè)結(jié)構(gòu)中繼承。唯一的例外是對(duì)應(yīng)的結(jié)構(gòu)(和其它類(lèi)型一樣)最終派生于類(lèi)System.Object。因此結(jié)構(gòu)也可以訪問(wèn)Object的方法。
在結(jié)構(gòu)中也可以重寫(xiě)Object中的方法——如ToString()方法。
結(jié)構(gòu)的繼承鏈?zhǔn)牵好總€(gè)結(jié)構(gòu)派生于System.ValueType類(lèi),System.ValueType類(lèi)有派生于System.Object。ValueType并沒(méi)有給Object添加任何成員,但提供了一些更適合結(jié)構(gòu)的實(shí)現(xiàn)方法。
注意,不能為結(jié)構(gòu)提供其它基類(lèi)。
3.結(jié)構(gòu)的構(gòu)造函數(shù)
為結(jié)構(gòu)定義構(gòu)造函數(shù)的方式與類(lèi)的方式相同,但不允許定義無(wú)參數(shù)的構(gòu)造函數(shù)。因?yàn)樵谝恍┖币?jiàn)的情況下,.NET運(yùn)行庫(kù)不能調(diào)用用戶(hù)提供的自定義無(wú)參數(shù)構(gòu)造函數(shù),因此Microsoft干脆采用禁止在C#的結(jié)構(gòu)內(nèi)使用無(wú)參數(shù)的構(gòu)造函數(shù)。
默認(rèn)構(gòu)造函數(shù)會(huì)隱式的把字段初始化,即使提供了其它帶參數(shù)的構(gòu)造函數(shù),也會(huì)先調(diào)用它。提供字段的初始值也不能繞過(guò)默認(rèn)構(gòu)造函數(shù)。下面代碼會(huì)編譯錯(cuò)誤:
struct PhoneCusStruct { public int CusId =0; }
如果PhoneCusStruct聲明為一個(gè)類(lèi),就不會(huì)報(bào)錯(cuò)了。
另外,可以像類(lèi)那樣為結(jié)構(gòu)提供Close()或Dispose()方法。
五.弱引用
在應(yīng)用程序代碼內(nèi)實(shí)例化一個(gè)類(lèi)或結(jié)構(gòu)時(shí),只要有代碼引用這個(gè)對(duì)象,就會(huì)形成強(qiáng)引用。這意味著垃圾回收器不會(huì)清理這個(gè)對(duì)象使用的內(nèi)存,一般而言這是好事,因?yàn)榭赡苄枰眠@個(gè)對(duì)象,但是如果這個(gè)對(duì)象很大,而且不經(jīng)常訪問(wèn)。這個(gè)時(shí)候可以創(chuàng)建對(duì)象的弱引用。
弱引用允許創(chuàng)建和使用對(duì)象,但在垃圾回收器運(yùn)行時(shí),就會(huì)回收對(duì)象并釋放內(nèi)存。由于存在潛在的Bug和性能問(wèn)題,一般不會(huì)這么做,但在特定情況下使用是合理的。
弱引用使用WeakReference類(lèi)創(chuàng)建。因?yàn)閷?duì)象可能在任意時(shí)刻被回收,所以引用該對(duì)象前必須確認(rèn)它的存在?! ?/p>
class MainEntryPoint { static void Main() { // Instantiate a weak reference to MathTest object WeakReference mathReference = new WeakReference(new MathTest()); MathTest math; if(mathReference.IsAlive) { math = mathReference.Target as MathTest; math.Value = 30; Console.WriteLine( "Value field of math variable contains " + math.Value); Console.WriteLine("Square of 30 is " + math.GetSquare()); } else { Console.WriteLine("Reference is not available."); } GC.Collect(); if(mathReference.IsAlive) { math = mathReference.Target as MathTest; } else { Console.WriteLine("Reference is not available."); } } } // Define a class named MathTest on which we will call a method class MathTest { public int Value; public int GetSquare() { return Value*Value; } public static int GetSquareOf(int x) { return x*x; } public static double GetPi() { return 3.14159; } }
六.部分類(lèi)
partial關(guān)鍵字允許把類(lèi),結(jié)構(gòu),接口放在多個(gè)文件中。
partial關(guān)鍵字的用法:把partial放在class,struct,interface前面即可。
如果聲明類(lèi)時(shí)使用了下面的關(guān)鍵字,這些關(guān)鍵字就必須應(yīng)用于同一個(gè)類(lèi)的所有部分:
public,private,protected,internal,abstract,sealed,new,一般約束
在把部分類(lèi)編譯后,類(lèi)的成員和繼承等會(huì)合并。
七.靜態(tài)類(lèi)
如果類(lèi)只包含靜態(tài)的方法和屬性,該類(lèi)就是靜態(tài)的。靜態(tài)類(lèi)在功能上與使用私有靜態(tài)構(gòu)造函數(shù)創(chuàng)建的類(lèi)相同。都不能創(chuàng)建靜態(tài)類(lèi)的實(shí)例。
使用static關(guān)鍵字,編譯器可以檢查用戶(hù)是否給該類(lèi)添加了實(shí)例成員。如果是,就會(huì)生成一個(gè)編譯錯(cuò)誤。這可以確保不創(chuàng)建靜態(tài)類(lèi)的實(shí)例。
static class PhoneCusStruct { public static void GetPhene() { } }
調(diào)用:PhoneCusStruct.GetPhene();
八.Object類(lèi)
前面提到,所有的.NET類(lèi)都派生自System.Object類(lèi).實(shí)際上,如果在定義類(lèi)的時(shí)候沒(méi)有指定基類(lèi),編譯器就會(huì)自動(dòng)假定這個(gè)類(lèi)派生自O(shè)bject類(lèi)。其實(shí)際意義在于,除了自己定義的方法和屬性等外,還可以訪問(wèn)Object定義的許多公有的和受保護(hù)的成員方法。這些方法可用于自己定義的其它類(lèi)中。
System.Object的方法:
- 1.GetHashCode():如果對(duì)象放在名為映射(散列表或字典)的數(shù)據(jù)結(jié)構(gòu)中,就可以使用這個(gè)方法。處理這些結(jié)構(gòu)的類(lèi)使用該方法可以確定把對(duì)象放在結(jié)構(gòu)的什么地方。如果希望把類(lèi)用作字典的一個(gè)鍵,就需要重寫(xiě)這個(gè)方法。(后面介紹字典時(shí)會(huì)詳細(xì)介紹)
- 2.Equals()和ReferenceEquals()方法:后面會(huì)詳細(xì)介紹。
- 3.Finalize()方法:在引用對(duì)象作為垃圾被回收以清理資源時(shí)調(diào)用它。Object中實(shí)現(xiàn)的Finalize()方法實(shí)際上什么也沒(méi)有做,因而被垃圾回收器忽略。如果對(duì)象擁有對(duì)未托管資源的引用,則在該對(duì)象被刪除時(shí),就需要?jiǎng)h除這些引用,此時(shí)一般要重寫(xiě)Finalize()方法。垃圾收集器不能直接刪除這些未托管資源的引用,因?yàn)樗回?fù)責(zé)托管的資源,于是它只能依賴(lài)用戶(hù)提供的Finalize()方法。垃圾收集器不能直接刪除這些未托管資源的引用,因?yàn)樗回?fù)責(zé)托管的資源,于是它只能依賴(lài)用戶(hù)提供的Finalize方法。
- 4.GetType()方法:這個(gè)方法返回從System.Type派生的類(lèi)的一個(gè)實(shí)例。這個(gè)對(duì)象可以提供對(duì)象成員所屬類(lèi)的很多信息。System.Type還提供了.NET的反射技術(shù)的入口。
- 5.MemberwiseClose()方法:這個(gè)方法復(fù)制對(duì)象,并返回對(duì)副本的一個(gè)引用(對(duì)于值類(lèi)型,就是一個(gè)裝箱的引用)。得到的副本是一個(gè)淺表復(fù)制,即它復(fù)制了類(lèi)中的所有值類(lèi)型。如果類(lèi)包含內(nèi)嵌的引用,就只復(fù)制引用,而不復(fù)制引用的對(duì)象。這個(gè)方法是受保護(hù)的,所以不能用于復(fù)制外部的對(duì)象(可以復(fù)制父類(lèi)的對(duì)象)。該方法不是虛方法,所以不能重寫(xiě)。
- 6.ToString()方法:是獲取對(duì)象的字符串表示的一種快捷方式。當(dāng)只需要快速獲取對(duì)象的內(nèi)容,以進(jìn)行調(diào)試時(shí),就可以使用這個(gè)方法。在數(shù)據(jù)的格式化方面,它幾乎沒(méi)有提供選擇,比如:在原則上日期可以表示為許多不同格式,但DateTime.ToString()沒(méi)有在這方面提供任何選擇。這個(gè)方法是虛方法,可以重寫(xiě)這個(gè)方法以返回這些類(lèi)型的正確字符串表示。
九.擴(kuò)展方法
如果有類(lèi)的源碼,繼承就可以給對(duì)象添加方法。但如果沒(méi)有源代碼,則可以使用擴(kuò)展方法,它允許改變一個(gè)類(lèi),但不需要該類(lèi)的源代碼。
擴(kuò)展方法是靜態(tài)方法,它是類(lèi)的一部分,但實(shí)際上沒(méi)有放在類(lèi)的源代碼中。假定PhoneCusStruct類(lèi)需要一個(gè)Add()方法,但不能修改源代碼,就可以創(chuàng)建一個(gè)靜態(tài)類(lèi),把Add()方法添加為一個(gè)靜態(tài)方法:
public static class PhoneExtension { public static void Add(this PhoneCusStruct phoneCusStruct,string phone) { // } }
注意擴(kuò)展方法的第一個(gè)參數(shù)是要擴(kuò)展的類(lèi)型,它放在this關(guān)鍵字的后面。這告訴編譯器,這個(gè)方法是PhoneCusStruct類(lèi)型的一部分。在這個(gè)例子中,PhoneCusStruct是要擴(kuò)展的類(lèi)型。在擴(kuò)展方法中,可以訪問(wèn)所擴(kuò)展類(lèi)型的所有公有方法和屬性。
調(diào)用:
PhoneCusStruct p =new PhoneCusStruct(); p.Add();//即使方法是靜態(tài)方法,也需要使用實(shí)例方法的語(yǔ)法。
如果擴(kuò)展方法與類(lèi)中的某個(gè)方法同名,就不會(huì)調(diào)用擴(kuò)展方法。類(lèi)中已有的任何實(shí)例方法優(yōu)先。
到此這篇關(guān)于C#類(lèi)和結(jié)構(gòu)的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 淺析C#中結(jié)構(gòu)與類(lèi)的區(qū)別
- 輕松學(xué)習(xí)C#的結(jié)構(gòu)和類(lèi)
- C#基礎(chǔ)語(yǔ)法:結(jié)構(gòu)和類(lèi)區(qū)別詳解
- C#中類(lèi)與結(jié)構(gòu)的區(qū)別實(shí)例分析
- C#枚舉類(lèi)型與結(jié)構(gòu)類(lèi)型實(shí)例解析
- 基于c# 類(lèi)、接口、結(jié)構(gòu)的聯(lián)系與區(qū)別詳解
- 深入c# 類(lèi)和結(jié)構(gòu)的區(qū)別總結(jié)詳解
- c#結(jié)構(gòu)和類(lèi)的相關(guān)介紹
相關(guān)文章
C#實(shí)現(xiàn)動(dòng)態(tài)圖標(biāo)閃爍顯示的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用C#實(shí)現(xiàn)動(dòng)態(tài)圖標(biāo)閃爍顯示的功能,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C#有一定的幫助,感興趣的小伙伴可以了解一下2022-12-12c#實(shí)現(xiàn)51單片機(jī)頻率計(jì)的代碼分享(數(shù)字頻率計(jì)設(shè)計(jì))
c#實(shí)現(xiàn)51單片機(jī)頻率計(jì)的代碼分享,大家參考使用吧2013-12-12C#創(chuàng)建windows系統(tǒng)用戶(hù)的方法
這篇文章主要介紹了C#創(chuàng)建windows系統(tǒng)用戶(hù)的方法,涉及C#操作用戶(hù)名、密碼、顯示名稱(chēng)、描述、是否強(qiáng)制修改密碼、密碼是否過(guò)期等技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04C#如何將List<string>轉(zhuǎn)換為L(zhǎng)ist<double>
這篇文章主要介紹了C#如何將List<string>轉(zhuǎn)換為L(zhǎng)ist<double>問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07C# DataTable與Model互轉(zhuǎn)的示例代碼
這篇文章主要介紹了C#DataTable與Model互轉(zhuǎn)的示例代碼,幫助大家更好的理解和使用c#,感興趣的朋友可以了解下2020-12-12Winform+.Net6實(shí)現(xiàn)圖片拖拽上傳功能
這篇文章主要為大家詳細(xì)介紹了如何使用WinformPictureBox控件+.Net6 WebApi實(shí)現(xiàn)圖片拖拽上傳功能,文中的示例代碼講解詳細(xì),感興趣的可以學(xué)習(xí)一下2023-09-09C#實(shí)現(xiàn)繪制鼠標(biāo)的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用C#實(shí)現(xiàn)繪制鼠標(biāo)的效果,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C#有一定的幫助,感興趣的小伙伴可以跟隨小編一起了解一下2022-12-12c#中利用委托反射將DataTable轉(zhuǎn)換為實(shí)體集的代碼
c#中利用委托反射將DataTable轉(zhuǎn)換為實(shí)體集的代碼,需要的朋友可以參考下2012-10-10