目錄:
創(chuàng)新互聯(lián)專注于企業(yè)營銷型網(wǎng)站、網(wǎng)站重做改版、資中網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5響應(yīng)式網(wǎng)站、商城開發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站建設(shè)公司、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為資中等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
【C#小知識(shí)】C#中一些易混淆概念總結(jié)
一,C#中結(jié)構(gòu)
在C#中可以使用struct關(guān)鍵字來定義一個(gè)結(jié)構(gòu),級別與類是一致的,寫在命名空間下面。
1)結(jié)構(gòu)中可以定義屬性,字段,方法和構(gòu)造函數(shù)。示例代碼如下:
//定義結(jié)構(gòu) struct Point { //定義字段 private int x; //封裝字段 public int X { get { return x; } set { x = value; } } //定義方法 public void Result() { } //定義構(gòu)造函數(shù) public Point(int n) { this.x = n; //Console.WriteLine(n); } }
那么,聲明類與結(jié)構(gòu)的區(qū)別有哪些呢?
①無論如何,C#編譯器都會(huì)為結(jié)構(gòu)生成無參數(shù)的構(gòu)造函數(shù);
當(dāng)我們顯式的定義無參數(shù)的構(gòu)造函數(shù),編譯時(shí)會(huì)報(bào)錯(cuò),結(jié)果如下:
編譯器告訴我們,結(jié)構(gòu)不能包含顯式的無參數(shù)的構(gòu)造函數(shù)
但是這樣編寫代碼時(shí),編譯器卻不報(bào)錯(cuò),代碼如下:
//這里可以調(diào)用無參數(shù)的構(gòu)造函數(shù)
Point p = new Point();
Console.WriteLine(p.GetType());
運(yùn)行結(jié)果如下:
雖然結(jié)構(gòu)不能顯式的聲明無參數(shù)的構(gòu)造函數(shù),但是程序員卻可以顯式的調(diào)用結(jié)構(gòu)的無參數(shù)的構(gòu)造函數(shù),說明C#編譯器無論如何都會(huì)為結(jié)構(gòu)生成無參數(shù)的構(gòu)造函數(shù)。
②結(jié)構(gòu)中的字段不能賦初始值;
③在結(jié)構(gòu)的構(gòu)造函數(shù)中必須要對結(jié)構(gòu)體的每一個(gè)字段賦值;
當(dāng)我們不聲明顯式的構(gòu)造函數(shù)時(shí),可以不對成員字段賦值,但是一旦聲明了構(gòu)造函數(shù),就要對所有的成員字段賦值
對所有的成員字段賦值,代碼如下:
//定義構(gòu)造函數(shù)
public Point(int n)
{
this.x = n;
//Console.WriteLine(n);
}
④在構(gòu)造函數(shù)中對屬性賦值不認(rèn)為對字段賦值,屬性不一定去操作字段;
所以在構(gòu)造函數(shù)中我們對字段賦初始值的時(shí)候,正確的代碼應(yīng)該是
//定義構(gòu)造函數(shù) public Point(int n) { //正確的可以對字段賦初始值 this.x = n; //在構(gòu)造函數(shù)中對屬性賦值,但是不一定操作字段 this.X = n; //Console.WriteLine(n); }
2)結(jié)構(gòu)體的數(shù)值類型問題
C#中的結(jié)構(gòu)是值類型,它的對象和成員字段是分配在棧中的,如下圖:
那么當(dāng)我們寫了如下的代碼,內(nèi)存中發(fā)生了什么呢?
//這里可以調(diào)用無參數(shù)的構(gòu)造函數(shù) Point p = new Point(); //為p的屬性賦值 p.X = 100; //將p賦值給Point新的對象p1 Point p1 = p;
Point p1=p發(fā)生了什么呢?情況如下:
聲明結(jié)構(gòu)體對象可以不使用“new”關(guān)鍵字如果不使用“new”關(guān)鍵字聲明結(jié)構(gòu)體對象,因?yàn)闆]有調(diào)用構(gòu)造函數(shù),這個(gè)時(shí)候結(jié)構(gòu)體對象是沒有值的。而結(jié)構(gòu)的構(gòu)造函數(shù)必須為結(jié)構(gòu)的所有字段賦值,所以通過"new"關(guān)鍵字創(chuàng)建結(jié)構(gòu)體對象的時(shí)候,這個(gè)對象被構(gòu)造函數(shù)初始化就有默認(rèn)的初始值了。實(shí)例代碼如下:
class Program { static void Main(string[] args) { //沒有辦法調(diào)用默認(rèn)的構(gòu)造函初始化 Point p; Console.WriteLine(p); //會(huì)調(diào)用默認(rèn)的構(gòu)造函數(shù)對的Point對象初始化 Point p1 = new Point(); Console.WriteLine(p1); Console.ReadKey(); } } //定義結(jié)構(gòu) struct Point { //定義時(shí)賦初始值,編譯器會(huì)報(bào)錯(cuò) private int x; }
編譯的時(shí)候會(huì)報(bào)錯(cuò):
3)結(jié)構(gòu)體不能使用自動(dòng)屬性
在第一篇文章我寫自動(dòng)屬性的時(shí)候,反編譯源代碼,知道自動(dòng)屬性,會(huì)生成一個(gè)默認(rèn)字段。而在結(jié)構(gòu)的構(gòu)造函數(shù)中需要對每一個(gè)字段賦值,但是編譯器不知道這個(gè)字段的名字。所以,沒有辦法使用自動(dòng)屬性。
那么什么時(shí)候定義類,什么時(shí)候定義結(jié)構(gòu)體呢?
首先我們都知道的是,棧的訪問速度相對于堆是比較快的。但是棧的空間相對于堆來說是比較小的。
①當(dāng)我們要表示一個(gè)輕量級的對象,就可以定義結(jié)構(gòu)體,提高訪問速度。
②根據(jù)傳值的影響來選擇,當(dāng)要傳遞的引用就定義類,當(dāng)要傳遞的是“拷貝”就定義結(jié)構(gòu)體。
二,關(guān)于GC(.NET的垃圾回收)
1)分配在棧中的空間變量,一旦出了該變量的作用域就會(huì)被CLR立即回收;如下代碼:
//定義值類型的n當(dāng),程序出了main函數(shù)后n在棧中占用的空間就會(huì)被CLR立即回收 static void Main(string[] args) { int n = 5; Console.WriteLine(n); }
2)分配在堆里面的對象,當(dāng)沒有任何變量的引用時(shí),這個(gè)對象就會(huì)被標(biāo)記為垃圾對象,等待垃圾回收器的回收;
GC會(huì)定時(shí)清理堆空間中的垃圾對象,這個(gè)時(shí)間頻率是程序員無法控制的,是由CLR決定的。所以,當(dāng)一個(gè)對象被標(biāo)記為垃圾對象的時(shí)候,不一定會(huì)被立即回收。
3)析構(gòu)函數(shù)
在回收垃圾對象的時(shí)候,析構(gòu)函數(shù)被GC自動(dòng)調(diào)用。主要是執(zhí)行一些清理善后工作。
析構(gòu)函數(shù)沒有訪問修飾符,不能有你參數(shù),使用“~”來修飾。 如下面的代碼示例:
class Program { //定義值類型的n當(dāng),程序出了main函數(shù)后n在棧中占用的空間就會(huì)被CLR立即回收 static void Main(string[] args) { int n = 5; OperateFile operate = new OperateFile(); operate.FileWrite(); //執(zhí)行完寫操作后,會(huì)調(diào)用該類的析構(gòu)函數(shù),釋放對文件對象的控制 //Console.WriteLine(n); } } //定義操作硬盤上文件上的類 class OperateFile { //定義寫文件的方法 public void FileWrite() { } //定義調(diào)用該類結(jié)束后,所要執(zhí)行的動(dòng)作 ~OperateFile() { //釋放對操作文件對象的控制 } }
三,靜態(tài)成員和實(shí)例成員的區(qū)別:
靜態(tài)成員是需要通過static關(guān)鍵字來修飾的,而實(shí)例成員不用static關(guān)鍵字修飾。他們區(qū)別如下代碼:
class Program { static void Main(string[] args) { //靜態(tài)成員屬于類,可以直接通過“類名.靜態(tài)成員”的方式訪問 Person.Run(); //實(shí)例成員屬于對象,需要通過“對象名.實(shí)例成員”來訪問 Person p = new Person(); p.Sing(); } } class Person { //靜態(tài)成員變量 private static int nAge; //實(shí)例成員變量 private string strName; public static void Run() { Console.WriteLine("我會(huì)奔跑!"); } public void Sing() { Console.WriteLine("我會(huì)唱歌"); } }
當(dāng)類第一次被加載的時(shí)候(就是該類第一次被加載到內(nèi)存當(dāng)中),該類下面的所有靜態(tài)的成員都會(huì)被加載。實(shí)例成員有多少對象,就會(huì)創(chuàng)建多少對象。
而靜態(tài)成員只被加載到靜態(tài)存儲(chǔ)區(qū),只被創(chuàng)建一次,且直到程序退出時(shí)才會(huì)被釋放。
看下面的代碼:
class Program { static void Main(string[] args) { Person p = new Person(); Person p1 = new Person(); Person p2 = new Person(); } } class Person { //靜態(tài)成員變量 private static int nAge; //實(shí)例成員變量 private string strName; public static void Run() { Console.WriteLine("我會(huì)奔跑!"); } public void Sing() { Console.WriteLine("我會(huì)唱歌"); } }
那么在內(nèi)存中發(fā)生了什么呢?如下圖:
由上面顯然可知,定義靜態(tài)的成員是可以影響程序的執(zhí)行效率的。那么什么時(shí)候定義靜態(tài)的成員變量呢?
①變量需要被共享的時(shí)候②方法需要被反復(fù)的調(diào)用的時(shí)候
2)在靜態(tài)方法中不能直接調(diào)用實(shí)例成員。
當(dāng)類第一次被加載的時(shí)候,靜態(tài)成員已經(jīng)被加載到靜態(tài)存儲(chǔ)區(qū),此時(shí)類的對象還有可能能沒有創(chuàng)建,所以靜態(tài)方法中不能調(diào)用類成員字段。實(shí)例代碼如下:
this和base關(guān)鍵字都不能在靜態(tài)方法中使用。
②可以創(chuàng)建類的對象指明對象的成員在靜態(tài)方法中操作,代碼如下:
public static void Run() { Person p = new Person(); p.strName = "強(qiáng)子"; Console.WriteLine("我會(huì)奔跑!"); }
③在實(shí)例成員中肯定可以調(diào)用靜態(tài)方法,因?yàn)檫@個(gè)時(shí)候靜態(tài)成員肯定存在,代碼如下:
public static void Run() { Person p = new Person(); p.strName = "強(qiáng)子"; Console.WriteLine("我會(huì)奔跑!"); } public void Sing() { //實(shí)例方法被調(diào)用的時(shí)候,對象實(shí)例一定會(huì)被創(chuàng)建,所以可以在實(shí)例方法中訪問實(shí)例的字段 this.strName = "子強(qiáng)"; strName = "子強(qiáng)"; //調(diào)用靜態(tài)成員 Run(); Console.WriteLine("我會(huì)唱歌"); }
靜態(tài)成員和實(shí)例成員的對比:
①生命周期不一樣
靜態(tài)成員只有在程序結(jié)束時(shí)才會(huì)釋放,而實(shí)例成員沒有對象引用時(shí)就會(huì)釋放
②內(nèi)存中存儲(chǔ)的位置不一樣
靜態(tài)成員存放在靜態(tài)存儲(chǔ)區(qū),實(shí)例成員在托管堆中。
四,靜態(tài)類
①靜態(tài)類被static關(guān)鍵字修飾
//定義兩個(gè)靜態(tài)類
staticclass Person { }
internalstaticclass Cat { }
②靜態(tài)類中只能生命靜態(tài)的成員變量,否則會(huì)報(bào)錯(cuò)(因?yàn)樵L問該實(shí)例成員的時(shí)候,類的對象可能還沒有被創(chuàng)建)
③靜態(tài)類中不能有實(shí)例的構(gòu)造函數(shù)(如果有實(shí)例的構(gòu)造函數(shù),則該靜態(tài)類能被實(shí)例化,都是靜態(tài)成員,沒有實(shí)例成員被調(diào)用)
正確的聲明方法:
static class Person { //private int nAge; private static string strName; static Person() { } }
④靜態(tài)類不能被繼承,反編譯剛才的兩個(gè)類,結(jié)果如下:
會(huì)發(fā)現(xiàn)靜態(tài)類的本質(zhì)是一個(gè)抽象密封類,所以不能被繼承和實(shí)例化。所以,靜態(tài)類的構(gòu)造函數(shù),不能有訪問修飾符
2)那么什么時(shí)候聲明靜態(tài)類呢?
如果這個(gè)類下面的所有成員的都需要被共享,可以把這個(gè)類聲明為靜態(tài)類。
且在一般對象中不能聲明靜態(tài)類型的變量(訪問該靜態(tài)變量時(shí),可能該對象還沒有被創(chuàng)建)。
3)靜態(tài)類的構(gòu)造函數(shù)
靜態(tài)類可以有靜態(tài)的構(gòu)造函數(shù)(且所有類都可以有靜態(tài)的構(gòu)造函數(shù)),如下代碼:
class Program { static void Main(string[] args) { Cat c; Cat c1 = new Cat(); Console.ReadKey(); } } class Cat { private int n; public string strName; //實(shí)例構(gòu)造函數(shù) public Cat() { Console.WriteLine("看誰先執(zhí)行2"); } //靜態(tài)構(gòu)造函數(shù) static Cat() { Console.WriteLine("看誰先執(zhí)行1"); } }
執(zhí)行結(jié)果如下:
由此我們可以知道,靜態(tài)的構(gòu)造函數(shù)會(huì)先于實(shí)例構(gòu)造函數(shù)執(zhí)行
且
//不執(zhí)行靜態(tài)構(gòu)造函數(shù)
Cat c;
當(dāng)我們在Main()函數(shù)中添加如下的代碼是:
static void Main(string[] args) { //不執(zhí)行靜態(tài)構(gòu)造函數(shù) Cat c; Cat c1 = new Cat(); Cat c2 = new Cat(); Console.ReadKey(); }
運(yùn)行結(jié)果如下:
說明靜態(tài)的構(gòu)造函數(shù)只執(zhí)行了一次。
---------------------------------------------分割線-----------------------------------------------
好吧這次的分享風(fēng)到此結(jié)束。希望對大家對理解C#基礎(chǔ)概念知識(shí)能有所幫助。
如果您覺得不錯(cuò),點(diǎn)擊右下角贊一下吧!您的支持,是我寫作的動(dòng)力!
畢業(yè)實(shí)習(xí)交流群:221376964。你也可以關(guān)注我的新浪微博進(jìn)行交流。