程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#中一些易混淆概念總結(三)---------結構,GC,靜態成員,靜態類

C#中一些易混淆概念總結(三)---------結構,GC,靜態成員,靜態類

編輯:C#入門知識

一,C#中結構   在C#中可以使用struct關鍵字來定義一個結構,級別與類是一致的,寫在命名空間下面。   1)結構中可以定義屬性,字段,方法和構造函數。示例代碼如下:   復制代碼 //定義結構     struct Point     {                   //定義字段         private int x;           //封裝字段         public int X         {             get { return x; }             set { x = value; }         }                  //定義方法         public void Result()         {                      }           //定義構造函數         public Point(int n)           {             this.x = n;             //Console.WriteLine(n);         }       } 復制代碼 那麼,聲明類與結構的區別有哪些呢?   ①無論如何,C#編譯器都會為結構生成無參數的構造函數;   當我們顯式的定義無參數的構造函數,編譯時會報錯,結果如下:       編譯器告訴我們,結構不能包含顯式的無參數的構造函數   但是這樣編寫代碼時,編譯器卻不報錯,代碼如下:    //這裡可以調用無參數的構造函數             Point p = new Point();             Console.WriteLine(p.GetType()); 運行結果如下:           雖然結構不能顯式的聲明無參數的構造函數,但是程序員卻可以顯式的調用結構的無參數的構造函數,說明C#編譯器無論如何都會為結構生成無參數的構造函數。       ②結構中的字段不能賦初始值;               ③在結構的構造函數中必須要對結構體的每一個字段賦值;   當我們不聲明顯式的構造函數時,可以不對成員字段賦值,但是一旦聲明了構造函數,就要對所有的成員字段賦值       對所有的成員字段賦值,代碼如下:        //定義構造函數         public Point(int n)         {             this.x = n;             //Console.WriteLine(n);         }     ④在構造函數中對屬性賦值不認為對字段賦值,屬性不一定去操作字段;           所以在構造函數中我們對字段賦初始值的時候,正確的代碼應該是   復制代碼          //定義構造函數         public Point(int n)         {             //正確的可以對字段賦初始值             this.x = n;               //在構造函數中對屬性賦值,但是不一定操作字段             this.X = n;             //Console.WriteLine(n);         }  復制代碼 2)結構體的數值類型問題   C#中的結構是值類型,它的對象和成員字段是分配在棧中的,如下圖:       那麼當我們寫了如下的代碼,內存中發生了什麼呢?            //這裡可以調用無參數的構造函數             Point p = new Point();             //為p的屬性賦值             p.X = 100;             //將p賦值給Point新的對象p1             Point p1 = p; Point p1=p發生了什麼呢?情況如下:           聲明結構體對象可以不使用“new”關鍵字如果不使用“new”關鍵字聲明結構體對象,因為沒有調用構造函數,這個時候結構體對象是沒有值的。而結構的構造函數必須為結構的所有字段賦值,所以通過"new"關鍵字創建結構體對象的時候,這個對象被構造函數初始化就有默認的初始值了。實例代碼如下:       復制代碼 class Program     {         static void Main(string[] args)         {            //沒有辦法調用默認的構造函初始化             Point p;             Console.WriteLine(p);               //會調用默認的構造函數對的Point對象初始化             Point p1 = new Point();             Console.WriteLine(p1);             Console.ReadKey();           }     }     //定義結構     struct Point     {         //定義時賦初始值,編譯器會報錯         private int x;     } 復制代碼     編譯的時候會報錯:           3)結構體不能使用自動屬性   在第一篇文章我寫自動屬性的時候,反編譯源代碼,知道自動屬性,會生成一個默認字段。而在結構的構造函數中需要對每一個字段賦值,但是編譯器不知道這個字段的名字。所以,沒有辦法使用自動屬性。       那麼什麼時候定義類,什麼時候定義結構體呢?   首先我們都知道的是,棧的訪問速度相對於堆是比較快的。但是棧的空間相對於堆來說是比較小的。   ①當我們要表示一個輕量級的對象,就可以定義結構體,提高訪問速度。   ②根據傳值的影響來選擇,當要傳遞的引用就定義類,當要傳遞的是“拷貝”就定義結構體。       二,關於GC(.NET的垃圾回收)   1)分配在棧中的空間變量,一旦出了該變量的作用域就會被CLR立即回收;如下代碼:           //定義值類型的n當,程序出了main函數後n在棧中占用的空間就會被CLR立即回收         static void Main(string[] args)         {             int n = 5;             Console.WriteLine(n);         } 2)分配在堆裡面的對象,當沒有任何變量的引用時,這個對象就會被標記為垃圾對象,等待垃圾回收器的回收;   GC會定時清理堆空間中的垃圾對象,這個時間頻率是程序員無法控制的,是由CLR決定的。所以,當一個對象被標記為垃圾對象的時候,不一定會被立即回收。   3)析構函數   在回收垃圾對象的時候,析構函數被GC自動調用。主要是執行一些清理善後工作。   析構函數沒有訪問修飾符,不能有你參數,使用“~”來修飾。 如下面的代碼示例:   復制代碼 class Program     {         //定義值類型的n當,程序出了main函數後n在棧中占用的空間就會被CLR立即回收         static void Main(string[] args)         {             int n = 5;               OperateFile operate = new OperateFile();               operate.FileWrite();             //執行完寫操作後,會調用該類的析構函數,釋放對文件對象的控制             //Console.WriteLine(n);         }     }       //定義操作硬盤上文件上的類     class OperateFile     {         //定義寫文件的方法         public void FileWrite()         { }           //定義調用該類結束後,所要執行的動作         ~OperateFile()         {         //釋放對操作文件對象的控制         }     } 復制代碼      三,靜態成員和實例成員的區別:   靜態成員是需要通過static關鍵字來修飾的,而實例成員不用static關鍵字修飾。他們區別如下代碼:       復制代碼 class Program     {         static void Main(string[] args)         {             //靜態成員屬於類,可以直接通過“類名.靜態成員”的方式訪問             Person.Run();               //實例成員屬於對象,需要通過“對象名.實例成員”來訪問             Person p = new Person();             p.Sing();         }     }       class Person     {         //靜態成員變量         private static int nAge;         //實例成員變量         private string strName;           public static void Run()         {             Console.WriteLine("我會奔跑!");         }           public void Sing()         {             Console.WriteLine("我會唱歌");         }     } 復制代碼 當類第一次被加載的時候(就是該類第一次被加載到內存當中),該類下面的所有靜態的成員都會被加載。實例成員有多少對象,就會創建多少對象。   而靜態成員只被加載到靜態存儲區,只被創建一次,且直到程序退出時才會被釋放。   看下面的代碼:   復制代碼 class Program     {         static void Main(string[] args)         {               Person p = new Person();             Person p1 = new Person();             Person p2 = new Person();           }     }              class Person     {         //靜態成員變量         private static int nAge;         //實例成員變量         private string strName;           public static void Run()         {             Console.WriteLine("我會奔跑!");         }           public void Sing()         {             Console.WriteLine("我會唱歌");         }     } 復制代碼 那麼在內存中發生了什麼呢?如下圖:           由上面顯然可知,定義靜態的成員是可以影響程序的執行效率的。那麼什麼時候定義靜態的成員變量呢?   ①變量需要被共享的時候②方法需要被反復的調用的時候        2)在靜態方法中不能直接調用實例成員。    當類第一次被加載的時候,靜態成員已經被加載到靜態存儲區,此時類的對象還有可能能沒有創建,所以靜態方法中不能調用類成員字段。實例代碼如下:           this和base關鍵字都不能在靜態方法中使用。   ②可以創建類的對象指明對象的成員在靜態方法中操作,代碼如下:    public static void Run()         {             Person p = new Person();             p.strName = "強子";             Console.WriteLine("我會奔跑!");         } ③在實例成員中肯定可以調用靜態方法,因為這個時候靜態成員肯定存在,代碼如下:   復制代碼  public static void Run()         {             Person p = new Person();             p.strName = "強子";             Console.WriteLine("我會奔跑!");         }           public void Sing()         {             //實例方法被調用的時候,對象實例一定會被創建,所以可以在實例方法中訪問實例的字段             this.strName = "子強";             strName = "子強";                 //調用靜態成員             Run();             Console.WriteLine("我會唱歌");         } 復制代碼 靜態成員和實例成員的對比:   ①生命周期不一樣   靜態成員只有在程序結束時才會釋放,而實例成員沒有對象引用時就會釋放   ②內存中存儲的位置不一樣   靜態成員存放在靜態存儲區,實例成員在托管堆中。       四,靜態類   ①靜態類被static關鍵字修飾    //定義兩個靜態類     static class Person     { }       internal static class Cat     { }     ②靜態類中只能生命靜態的成員變量,否則會報錯(因為訪問該實例成員的時候,類的對象可能還沒有被創建)       ③靜態類中不能有實例的構造函數(如果有實例的構造函數,則該靜態類能被實例化,都是靜態成員,沒有實例成員被調用)           正確的聲明方法:   復制代碼       static class Person     {         //private int nAge;         private static string strName;           static Person()         {          }     } 復制代碼     ④靜態類不能被繼承,反編譯剛才的兩個類,結果如下:           會發現靜態類的本質是一個抽象密封類,所以不能被繼承和實例化。所以,靜態類的構造函數,不能有訪問修飾符       2)那麼什麼時候聲明靜態類呢?   如果這個類下面的所有成員的都需要被共享,可以把這個類聲明為靜態類。   且在一般對象中不能聲明靜態類型的變量(訪問該靜態變量時,可能該對象還沒有被創建)。           3)靜態類的構造函數   靜態類可以有靜態的構造函數(且所有類都可以有靜態的構造函數),如下代碼:   復制代碼 class Program     {         static void Main(string[] args)         {             Cat c;             Cat c1 = new Cat();               Console.ReadKey();         }     }       class Cat     {         private int n;         public string strName;           //實例構造函數         public Cat()         {             Console.WriteLine("看誰先執行2");         }           //靜態構造函數         static Cat()         {             Console.WriteLine("看誰先執行1");         }       } 復制代碼 執行結果如下:           由此我們可以知道,靜態的構造函數會先於實例構造函數執行   且               //不執行靜態構造函數             Cat c; 當我們在Main()函數中添加如下的代碼是:   復制代碼 static void Main(string[] args)         {             //不執行靜態構造函數             Cat c;             Cat c1 = new Cat();             Cat c2 = new Cat();               Console.ReadKey();         } 復制代碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved