C# 篇基礎知識4——.NET的基礎概念


C#語言是與微軟的.NET框架緊密地聯系在一起的,而.NET框架是微軟.NET戰略的核心,為了更好的理解C#語言,我們必須了解一些.NET框架的基本知識。.NET框架是為開發應用程序推出的一個編程平台,它主要為編寫應用程序提供兩方面的支持,一是它管理代碼的執行過程,二是它為代碼提供類庫支持。

(1)公共語言運行時CLR

.NET平台下編寫的程序一般都在公共語言運行時(Common Language Runtime,CLR)的管理下運行,它負責運行代碼,確保代碼的安全性和准確性,又負責內存管理、線程調度等核心服務,通常把在CLR控制下運行的代碼稱為托管代碼(Managed Code)。

(2)FCL類庫

.NET為我們提供了一個內容豐富的.NET框架基礎類庫(Framework Class Library,FCL),如果將C#程序比喻為一座大廈,那么大廈的設計思想就是面向對象編程,而建築大廈的材料則來自於.NET框架基礎類庫,我們可以像使用鋼筋、水泥、磚塊一樣使用FCL中的類構建應用程序大廈。因此學習C#的一個重點就是學習FCL類庫中的常用類。

1. C#為何采用兩次編譯

1)為了提高性能

因為JIT編譯中程序運行時發生,這時編譯器已經知道系統使用何種類型的CPU,可以針對該CPU特性在JIT編譯過程中進行代碼優化(比如可以更高效地利用CPU 的寄存器,在適當的情況下實施低級代碼優化(常量重疊、拷貝復制、取消范圍檢查、取消常規副表達式以及方法內聯等),可以在代碼執行期間監控當前的物理和虛擬內存需求從而更高效地利用內存等),另外,雖然程序運行代碼首次由中間語言編成為機器語言時,性能會稍有損失,但由於中間語言可以非常快速地轉換為機器語言,並且被優化的程序代碼往往被成百上千次使用,第一次編譯造成的這點性能損失顯得微不足道,C#兩次編譯的優勢就顯現出來了。

2)語言互操作性

MSIL 為不同編程語言的互操作性提供了可能,不同語言編寫的組件最終都編譯為中間語言,然后組成一個完整的程序。這樣就能讓使用C#、Visual Basic、Visual C++的開發人員一起完成同一項目。

3)平台無關性

Mono項目,以及現在正式推出的.NET Core,使基於.NET程序能夠運行在各種操作系統平台上。

2.強數據類型

中間語言是基於強數據類型的,即每一個變量都有明確的數據類型。雖然有時強數據類型會降低性能,但會在語言的互操作性、垃圾回收、安全性、應用程序域等方面獲得更多的好處。

1)通用類型系統

由於不同編程語言的數據類型和語法都有所不同,由此.NET為中間語言制定了一個通用類型系統(Common Type System,CTS),它定義了一系列標准的基本數據類型,不管你用什么語言編寫程序,程序中的變量最終都被轉換為這些基本數據類型。

通用類型系統不僅指定了基本數據類型,還定義了一個內容豐富的類型層次結構,允許用戶通過這些基本類型構造自己需要的類型,比如數組、結構、類等。如圖所示。

(2)公共語言規范

除了通用類型系統外,.NET 框架還制定了一套公共語言規范(Common Language Specification,CLS)。每種語言都有一套自己的語法規則集,而恰恰CLS 就是這些集合的交集,所以它能被.NET 上的所有語言支持。通俗地說就是公共語言規范中的語法規則在所有.NET 語言中都成立。如此一來,符合CLS 規范的代碼轉換為中間語言后就可以被.NET 上任何語言訪問,從而確保不同語言的互操作性。編寫不符合CLS 規范的代碼是完全可以的,但這時就不能保證不同語言間的互操作性。所以在編寫用於共享的類時,一般只用完全兼容CLS 的代碼,以增強類的交互性;在私有類中,可以編寫非CLS 代碼,因為私有類不用來交互。C#中不兼容CLS 的特性非常少。

3. 類型的判定、命名空間和裝箱拆箱

 

(1)類型的判定

通過下面幾種方法,可以輕易獲取變量的類型信息:

Sizeof()運算符,通過它可獲知數據類型在內存中占用幾個字節。如sizeof(int)。

typeof(),通過它可以獲取數據類型的通用數據類型名,此運算符的參數只能是類型。如typeof(int)得到System.Int32。

GetType(),如果想要獲取某個變量的類型,需要使用該變量的GetType()方法,此方法繼承自Object類,例如對於int t;string str=t.GetType();。

結合 GetType()方法和typeof 運算符,我們就可以檢驗變量是否為某種類型。kitty.GetType() == typeof(Cat))。is 運算符,is 運算符檢驗某個對象是否為某種類型,而且用法更為簡潔,例如if(kitty is Cat){}。這兩種判斷變量類型的方式有一點區別,即當對象和類型之間是繼承關系時,is運算符仍然返回true。

(2)命名空間

命名空間是用來組織類的,它避免了重名的問題。System是.Net預定義的一個命名空間,它包含了大量常用類。如果在文件開頭使用了using 語句,在使用相應命名空間中的類時,就不必添加命名空間前綴了。.NET 建議在大多數情況下,都至少要提供兩個嵌套的命名空間,第一個是公司名,第二個是技術名或軟件名。這么做可以盡量保證不與其它組織編寫的命名空間沖突。例如Microsoft.Win32. Registry類。

(3)裝箱拆箱

在C#中,一切變量都可以看作對象,所有值類型數據都可以通過隱式的裝箱操作轉換為引用型對象,裝箱操作的優點在於可以像操作對象那樣操作值類型變量。操作方法例如:int n=3;Object obj=n;。對於裝入箱中的值類型數據,可以通過拆箱(Unboxing)操作釋放出來,拆箱操作要用顯式轉換,例如int i=(int)obj;。當一個方法的參數類型不能確定時,裝箱操作就非常有用,例如一個用來存儲各種物品的倉庫類:

class Storehouse

{ public Object [] items;

  private int count;

 

  public Storehouse(int size)

{ items=new Object[size];count=0;}

 

public void Add(Object obj)

{

   if(count<items.Length)

   {  items[count]=obj;

      count++;}

   else

   {Console.WriteLine(“倉庫已滿”);}

}

}

3.異常

異常(Exception)就是程序執行期間發生的問題,原因並不總在程序員,比如用戶輸入了非法數據,要讀取的文件不存在等等。如果不處理這些異常,程序可能會崩潰,但如果在程序中過多的處理這些異常,會使程序結構不清晰。為此,C#為我們提供了一套完美的方案,在程序主線之外處理異常,不但使程序更加健壯、更加容錯,而且保持了程序結構的清晰。

(1)捕獲異常——try-catch結構

一般情況下我們需要對可能的異常情況進行處理,這時就需要捕獲並處理異常。在C#中用try-catch 結構捕獲並處理異常。把可能出現異常的語句放在 try 塊中,把異常發生后的處理代碼放在catch 塊中。產生異常后CLR會在try 塊后面尋找匹配的catch 塊,並執行該塊中的語句。

(2)收尾工作——try-catch-finally結構

在某些問題中,不論是否出現異常,都要進行一些收尾工作,這些收尾工作常放在finally 塊中。可能發生異常的代碼放在try 塊中,異常處理代碼放在catch 塊中,不管是否發生異常,

程序都要執行finally 塊中的代碼。

 

例如,程序經常動態申請資源,比如打開某個文件,並從中讀寫數據。操作系統通常不允許多個程序同時讀寫同一個文件,所以讀寫完文件后,應及時關閉文件(即釋放資源),以便其它程序使用。如果不關閉文件,就會發生資源泄露。這些釋放資源的代碼通常放在finally 塊,不管是否發生異常,資源均被釋放。

注意:try 塊和catch 塊、finally 塊一起構成try 語句,try 塊是關鍵字try 后用大括號括起來的語句塊。如果 try 塊后面有catch 塊,則finally 塊是可選的;如果try 塊后面沒有catch 塊,則必須跟一個finally 塊;如果既有catch 塊又有finally 塊,finally 塊必須放在最后。

(3)拋出異常——throw語句

.NET 可以自動檢測並拋出常見異常,但有時我們需要人工拋出異常。

try

{

Console.Write("請輸入一個0 到10 之間的整數:");

int number = Convert.ToInt32(Console.ReadLine());

if (number < 0 || number > 10)

{

throw new ArgumentOutOfRangeException();

}

else

{

Console.WriteLine("你輸入的整數是:{0}", number);

}

}

catch (ArgumentOutOfRangeException)

{

Console.WriteLine("你輸入的整數超出范圍!");

}

finally

{ Console.WriteLine("謝謝!"); } }

在C#中使用thow 語句拋出異常,其一般格式如下圖所示,該語句先通過new 運算符創建異常類ArgumentOutOf RangeException 的一個對象,然后通過throw 語句拋出。當程序遇到throw 語句時,會立即停止執行try 塊中的語句,轉而執行匹配的catch 塊中的語句。

4.NET中的異常類

(1)常見異常類

.NET 提供了豐富的異常類型,所有的異常類型都派生於Exception 類。下圖列舉出了一些常見的異常。

類大多在System 命名空間中,而IOException 類及其派生類在System.IO命名空間中(這個命名空間用於處理數據的讀寫)。一般情況下,異常沒有特定的命名空間,哪個類生成異常,異常就放在哪個類所在的命名空間。Exception 類有兩個非常重要的派生類——SystemException 和ApplicationException。.NET 中預定義的異常類都派生於SystemException 類,而用戶自定義的異常類都應派生於ApplicationException 類。由於派生類對象屬於基類,所以當有多個 catch 塊時,要按從具體到一般的順序由上往下排列,基類必須放在最后,否則捕捉派生類異常的catch 塊永遠沒機會被執行。

 (2)獲取方法的異常幫助信息

還可以不給catch 塊指定異常類型,這時它可以捕獲任何異常。如想查找方法Convert.ToInt32(String)的異常,可以直接在vs ide中按F1,即可在幫助中找到Convert.ToInt32 (String)方法的文檔,其中會有一節用來描述和該方法相關的異常。

(3)異常類的屬性

Exception類和其他一般類一樣,有幾個公有屬性,通過這些屬性可以非常方便的了解異常信息。其中比較重要的兩個屬性是Message和StackTrace。屬性Message用於描述異常的原因,屬性StackTrace用於描述異常的堆棧信息,即發生異常位置。例如

catch (DivideByZeroException e)

{ Console.WriteLine("Message:" + e.Message);

Console.WriteLine("StackTrace:" + e.StackTrace); }

(4)自定義異常

大多數情況下,我們使用.Net 預定義的異常類,必要時我們也可以針對程序中的問題創建新的異常類。用戶定義的異常類都直接或間接繼承於ApplicationException 類。例如:

Class NegativeNumberException:ApplicatioinException

{

  //屬性Message 被直接初始化為字符串"對負數進行非法操作"

public NegativeNumberException():base(“對負數進行非法操作”){}

  public NegativeNumberException(string message):base(message){}

}

異常類的名稱最好和故障相關聯,並以“Exception”結尾。

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM