您好,登錄后才能下訂單哦!
目錄:
【C#小知識】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"關鍵字創建結構體對象的時候,這個對象被構造函數初始化就有默認的初始值了。實例代碼如下:
編譯的時候會報錯:
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自動調用。主要是執行一些清理善后工作。
析構函數沒有訪問修飾符,不能有你參數,使用“~”來修飾。 如下面的代碼示例:
三,靜態成員和實例成員的區別:
靜態成員是需要通過static關鍵字來修飾的,而實例成員不用static關鍵字修飾。他們區別如下代碼:
當類第一次被加載的時候(就是該類第一次被加載到內存當中),該類下面的所有靜態的成員都會被加載。實例成員有多少對象,就會創建多少對象。
而靜態成員只被加載到靜態存儲區,只被創建一次,且直到程序退出時才會被釋放。
看下面的代碼:
那么在內存中發生了什么呢?如下圖:
由上面顯然可知,定義靜態的成員是可以影響程序的執行效率的。那么什么時候定義靜態的成員變量呢?
①變量需要被共享的時候②方法需要被反復的調用的時候
2)在靜態方法中不能直接調用實例成員。
當類第一次被加載的時候,靜態成員已經被加載到靜態存儲區,此時類的對象還有可能能沒有創建,所以靜態方法中不能調用類成員字段。實例代碼如下:
this和base關鍵字都不能在靜態方法中使用。
②可以創建類的對象指明對象的成員在靜態方法中操作,代碼如下:
public static void Run() { Person p = new Person(); p.strName = "強子"; Console.WriteLine("我會奔跑!"); }
③在實例成員中肯定可以調用靜態方法,因為這個時候靜態成員肯定存在,代碼如下:
靜態成員和實例成員的對比:
①生命周期不一樣
靜態成員只有在程序結束時才會釋放,而實例成員沒有對象引用時就會釋放
②內存中存儲的位置不一樣
靜態成員存放在靜態存儲區,實例成員在托管堆中。
四,靜態類
①靜態類被static關鍵字修飾
//定義兩個靜態類
staticclass Person { }
internalstaticclass Cat { }
②靜態類中只能生命靜態的成員變量,否則會報錯(因為訪問該實例成員的時候,類的對象可能還沒有被創建)
③靜態類中不能有實例的構造函數(如果有實例的構造函數,則該靜態類能被實例化,都是靜態成員,沒有實例成員被調用)
正確的聲明方法:
④靜態類不能被繼承,反編譯剛才的兩個類,結果如下:
會發現靜態類的本質是一個抽象密封類,所以不能被繼承和實例化。所以,靜態類的構造函數,不能有訪問修飾符
2)那么什么時候聲明靜態類呢?
如果這個類下面的所有成員的都需要被共享,可以把這個類聲明為靜態類。
且在一般對象中不能聲明靜態類型的變量(訪問該靜態變量時,可能該對象還沒有被創建)。
3)靜態類的構造函數
靜態類可以有靜態的構造函數(且所有類都可以有靜態的構造函數),如下代碼:
執行結果如下:
由此我們可以知道,靜態的構造函數會先于實例構造函數執行
且
//不執行靜態構造函數
Cat c;
當我們在Main()函數中添加如下的代碼是:
運行結果如下:
說明靜態的構造函數只執行了一次。
---------------------------------------------分割線-----------------------------------------------
好吧這次的分享風到此結束。希望對大家對理解C#基礎概念知識能有所幫助。
如果您覺得不錯,點擊右下角贊一下吧!您的支持,是我寫作的動力!
畢業實習交流群:221376964。你也可以關注我的新浪微博進行交流。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。