.Net 框架(.Net Framework)
.Net 框架是一個創新的平臺,能幫您編寫出下面類型的應用程序:
- Windows 應用程序
- Web 應用程序
- Web 服務
.Net 框架由一個巨大的代碼庫組成,用於 C# 等客戶端語言。下面列出一些 .Net 框架的組件:
- 公共語言運行庫(Common Language Runtime - CLR)
- .Net 框架類庫(.Net Framework Class Library)
- 公共語言規範(Common Language Specification)
- 通用類型系統(Common Type System)
- 元數據(Metadata)和組件(Assemblies)
- Windows 窗體(Windows Forms)
- ASP.Net 和 ASP.Net AJAX
- ADO.Net
- Windows 工作流基礎(Windows Workflow Foundation - WF)
- Windows 顯示基礎(Windows Presentation Foundation)
- Windows 通信基礎(Windows Communication Foundation - WCF)
- LINQ
C# 封裝
抽象和封裝是面向對象程序設計的相關特性。抽象允許相關信息可視化,封裝則使程序員實現所需級別的抽象。
封裝使用 訪問修飾符 來實現。一個 訪問修飾符 定義了一個類成員的範圍和可見性。C# 支持的訪問修飾符如下所示:
- Public
- Private
- Protected
- Internal
- Protected internal
C# 方法
<Access Specifier> <Return Type> <Method Name>(Parameter List) { Method Body }
using System; namespace CalculatorApplication { class NumberManipulator { public int FindMax(int num1, int num2) { /* 局部變量聲明 */ int result; if (num1 > num2) result = num1; else result = num2; return result; } } class Test { static void Main(string[] args) { /* 局部變量定義 */ int a = 100; int b = 200; int ret; NumberManipulator n = new NumberManipulator(); //調用 FindMax 方法 ret = n.FindMax(a, b); Console.WriteLine("最大值是: {0}", ret ); Console.ReadLine(); } } }
按引用傳遞參數
引用參數是一個對變量的內存位置的引用。當按引用傳遞參數時,與值參數不同的是,它不會爲這些參數創建一個新的存儲位置。引用參數表示與提供給方法的實際參數具有相同的內存位置。
在 C# 中,使用 ref 關鍵字聲明引用參數。
按輸出傳遞參數
return 語句可用於只從函數中返回一個值。但是,可以使用 輸出參數 來從函數中返回兩個值。輸出參數會把方法輸出的數據賦給自己,其他方面與引用參數相似。
下面的實例演示了這點:
using System; namespace CalculatorApplication { class NumberManipulator { public void getValue(out int x ) { int temp = 5; x = temp; } static void Main(string[] args) { NumberManipulator n = new NumberManipulator(); /* 局部變量定義 */ int a = 100; Console.WriteLine("在方法調用之前,a 的值: {0}", a); /* 調用函數來獲取值 */ n.getValue(out a); Console.WriteLine("在方法調用之後,a 的值: {0}", a); Console.ReadLine(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
在方法調用之前,a 的值: 100 在方法調用之後,a 的值: 5
提供給輸出參數的變量不需要賦值。當需要從一個參數沒有指定初始值的方法中返回值時,輸出參數特別有用。請看下面的實例,來理解這一點:
using System; namespace CalculatorApplication { class NumberManipulator { public void getValues(out int x, out int y ) { Console.WriteLine("請輸入第一個值: "); x = Convert.ToInt32(Console.ReadLine()); Console.WriteLine("請輸入第二個值: "); y = Convert.ToInt32(Console.ReadLine()); } static void Main(string[] args) { NumberManipulator n = new NumberManipulator(); /* 局部變量定義 */ int a , b; /* 調用函數來獲取值 */ n.getValues(out a, out b); Console.WriteLine("在方法調用之後,a 的值: {0}", a); Console.WriteLine("在方法調用之後,b 的值: {0}", b); Console.ReadLine(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果(取決於用戶輸入):
請輸入第一個值: 7 請輸入第二個值: 8 在方法調用之後,a 的值: 7 在方法調用之後,b 的值: 8
C# 字符串(String)
在 C# 中,您可以使用字符數組來表示字符串,但是,更常見的做法是使用 string 關鍵字來聲明一個字符串變量。string 關鍵字是 System.String 類的別名。
創建 String 對象
您可以使用以下方法之一來創建 string 對象:
- 通過給 String 變量指定一個字符串
- 通過使用 String 類構造函數
- 通過使用字符串串聯運算符( + )
- 通過檢索屬性或調用一個返回字符串的方法
- 通過格式化方法來轉換一個值或對象爲它的字符串表示形式
下面的實例演示了這點:
using System; namespace StringApplication { class Program { static void Main(string[] args) { //字符串,字符串連接 string fname, lname; fname = "Rowan"; lname = "Atkinson"; string fullname = fname + lname; Console.WriteLine("Full Name: {0}", fullname); //通過使用 string 構造函數 char[] letters = { 'H', 'e', 'l', 'l','o' }; string greetings = new string(letters); Console.WriteLine("Greetings: {0}", greetings); //方法返回字符串 string[] sarray = { "Hello", "From", "Tutorials", "Point" }; string message = String.Join(" ", sarray); Console.WriteLine("Message: {0}", message); //用於轉化值的格式化方法 DateTime waiting = new DateTime(2012, 10, 10, 17, 58, 1); string chat = String.Format("Message sent at {0:t} on {0:D}", waiting); Console.WriteLine("Message: {0}", chat); Console.ReadKey() ; } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
Full Name: Rowan Atkinson Greetings: Hello Message: Hello From Tutorials Point Message: Message sent at 5:58 PM on Wednesday, October 10, 2012
String 類的屬性
String 類有以下兩個屬性:
序號 | 屬性名稱 & 描述 |
---|---|
1 |
Chars 在當前 String 對象中獲取 Char 對象的指定位置。 |
2 |
Length 在當前的 String 對象中獲取字符數。 |
String 類的方法
String 類有許多方法用於 string 對象的操作。下面的表格提供了一些最常用的方法:
序號 | 方法名稱 & 描述 |
---|---|
1 |
public static int Compare( string strA, string strB ) 比較兩個指定的 string 對象,並返回一個表示它們在排列順序中相對位置的整數。該方法區分大小寫。 |
2 |
public static int Compare( string strA, string strB, bool ignoreCase ) 比較兩個指定的 string 對象,並返回一個表示它們在排列順序中相對位置的整數。但是,如果布爾參數爲真時,該方法不區分大小寫。 |
3 |
public static string Concat( string str0, string str1 ) 連接兩個 string 對象。 |
4 |
public static string Concat( string str0, string str1, string str2 ) 連接三個 string 對象。 |
5 |
public static string Concat( string str0, string str1, string str2, string str3 ) 連接四個 string 對象。 |
6 |
public bool Contains( string value ) 返回一個表示指定 string 對象是否出現在字符串中的值。 |
7 |
public static string Copy( string str ) 創建一個與指定字符串具有相同值的新的 String 對象。 |
8 |
public void CopyTo( int sourceIndex, char[] destination, int destinationIndex, int count ) 從 string 對象的指定位置開始複製指定數量的字符到 Unicode 字符數組中的指定位置。 |
9 |
public bool EndsWith( string value ) 判斷 string 對象的結尾是否匹配指定的字符串。 |
10 |
public bool Equals( string value ) 判斷當前的 string 對象是否與指定的 string 對象具有相同的值。 |
11 |
public static bool Equals( string a, string b ) 判斷兩個指定的 string 對象是否具有相同的值。 |
12 |
public static string Format( string format, Object arg0 ) 把指定字符串中一個或多個格式項替換爲指定對象的字符串表示形式。 |
13 |
public int IndexOf( char value ) 返回指定 Unicode 字符在當前字符串中第一次出現的索引,索引從 0 開始。 |
14 |
public int IndexOf( string value ) 返回指定字符串在該實例中第一次出現的索引,索引從 0 開始。 |
15 |
public int IndexOf( char value, int startIndex ) 返回指定 Unicode 字符從該字符串中指定字符位置開始搜索第一次出現的索引,索引從 0 開始。 |
16 |
public int IndexOf( string value, int startIndex ) 返回指定字符串從該實例中指定字符位置開始搜索第一次出現的索引,索引從 0 開始。 |
17 |
public int IndexOfAny( char[] anyOf ) 返回某一個指定的 Unicode 字符數組中任意字符在該實例中第一次出現的索引,索引從 0 開始。 |
18 |
public int IndexOfAny( char[] anyOf, int startIndex ) 返回某一個指定的 Unicode 字符數組中任意字符從該實例中指定字符位置開始搜索第一次出現的索引,索引從 0 開始。 |
19 |
public string Insert( int startIndex, string value ) 返回一個新的字符串,其中,指定的字符串被插入在當前 string 對象的指定索引位置。 |
20 |
public static bool IsNullOrEmpty( string value ) 指示指定的字符串是否爲 null 或者是否爲一個空的字符串。 |
21 |
public static string Join( string separator, params string[] value ) 連接一個字符串數組中的所有元素,使用指定的分隔符分隔每個元素。 |
22 |
public static string Join( string separator, string[] value, int startIndex, int count ) 鏈接一個字符串數組中的指定元素,使用指定的分隔符分隔每個元素。 |
23 |
public int LastIndexOf( char value ) 返回指定 Unicode 字符在當前 string 對象中最後一次出現的索引位置,索引從 0 開始。 |
24 |
public int LastIndexOf( string value ) 返回指定字符串在當前 string 對象中最後一次出現的索引位置,索引從 0 開始。 |
25 |
public string Remove( int startIndex ) 移除當前實例中的所有字符,從指定位置開始,一直到最後一個位置爲止,並返回字符串。 |
26 |
public string Remove( int startIndex, int count ) 從當前字符串的指定位置開始移除指定數量的字符,並返回字符串。 |
27 |
public string Replace( char oldChar, char newChar ) 把當前 string 對象中,所有指定的 Unicode 字符替換爲另一個指定的 Unicode 字符,並返回新的字符串。 |
28 |
public string Replace( string oldValue, string newValue ) 把當前 string 對象中,所有指定的字符串替換爲另一個指定的字符串,並返回新的字符串。 |
29 |
public string[] Split( params char[] separator ) 返回一個字符串數組,包含當前的 string 對象中的子字符串,子字符串是使用指定的 Unicode 字符數組中的元素進行分隔的。 |
30 |
public string[] Split( char[] separator, int count ) 返回一個字符串數組,包含當前的 string 對象中的子字符串,子字符串是使用指定的 Unicode 字符數組中的元素進行分隔的。int 參數指定要返回的子字符串的最大數目。 |
31 |
public bool StartsWith( string value ) 判斷字符串實例的開頭是否匹配指定的字符串。 |
32 |
public char[] ToCharArray() 返回一個帶有當前 string 對象中所有字符的 Unicode 字符數組。 |
33 |
public char[] ToCharArray( int startIndex, int length ) 返回一個帶有當前 string 對象中所有字符的 Unicode 字符數組,從指定的索引開始,直到指定的長度爲止。 |
34 |
public string ToLower() 把字符串轉換爲小寫並返回。 |
35 |
public string ToUpper() 把字符串轉換爲大寫並返回。 |
36 |
public string Trim() 移除當前 String 對象中的所有前導空白字符和後置空白字符。 |
上面的方法列表並不詳盡,請訪問 MSDN 庫,查看完整的方法列表和 String 類構造函數。
實例
下面的實例演示了上面提到的一些方法:
比較字符串
using System; namespace StringApplication { class StringProg { static void Main(string[] args) { string str1 = "This is test"; string str2 = "This is text"; if (String.Compare(str1, str2) == 0) { Console.WriteLine(str1 + " and " + str2 + " are equal."); } else { Console.WriteLine(str1 + " and " + str2 + " are not equal."); } Console.ReadKey() ; } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
This is test and This is text are not equal.
字符串包含字符串:
using System; namespace StringApplication { class StringProg { static void Main(string[] args) { string str = "This is test"; if (str.Contains("test")) { Console.WriteLine("The sequence 'test' was found."); } Console.ReadKey() ; } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
The sequence 'test' was found.
獲取子字符串:
using System; namespace StringApplication { class StringProg { static void Main(string[] args) { string str = "Last night I dreamt of San Pedro"; Console.WriteLine(str); string substr = str.Substring(23); Console.WriteLine(substr); } Console.ReadKey() ; } }
當上面的代碼被編譯和執行時,它會產生下列結果:
San Pedro
連接字符串:
using System; namespace StringApplication { class StringProg { static void Main(string[] args) { string[] starray = new string[]{"Down the way nights are dark", "And the sun shines daily on the mountain top", "I took a trip on a sailing ship", "And when I reached Jamaica", "I made a stop"}; string str = String.Join("\n", starray); Console.WriteLine(str); } Console.ReadKey() ; } }
當上面的代碼被編譯和執行時,它會產生下列結果:
Down the way nights are dark And the sun shines daily on the mountain top I took a trip on a sailing ship And when I reached Jamaica I made a stop
類 vs 結構
類和結構有以下幾個基本的不同點:
- 類是引用類型,結構是值類型。
- 結構不支持繼承。
- 結構不能聲明默認的構造函數。
針對上述討論,讓我們重寫前面的實例:
using System; struct Books { private string title; private string author; private string subject; private int book_id; public void getValues(string t, string a, string s, int id) { title = t; author = a; subject = s; book_id = id; } public void display() { Console.WriteLine("Title : {0}", title); Console.WriteLine("Author : {0}", author); Console.WriteLine("Subject : {0}", subject); Console.WriteLine("Book_id :{0}", book_id); } }; public class testStructure { public static void Main(string[] args) { Books Book1 = new Books(); /* 聲明 Book1,類型爲 Book */ Books Book2 = new Books(); /* 聲明 Book2,類型爲 Book */ /* book 1 詳述 */ Book1.getValues("C Programming", "Nuha Ali", "C Programming Tutorial",6495407); /* book 2 詳述 */ Book2.getValues("Telecom Billing", "Zara Ali", "Telecom Billing Tutorial", 6495700); /* 打印 Book1 信息 */ Book1.display(); /* 打印 Book2 信息 */ Book2.display(); Console.ReadKey(); } }
當上面的代碼被編譯和執行時,它會產生下列結果:
Title : C Programming Author : Nuha Ali Subject : C Programming Tutorial Book_id : 6495407 Title : Telecom Billing Author : Zara Ali Subject : Telecom Billing Tutorial Book_id : 6495700
C# 枚舉(Enum)
實例
下面的實例演示了枚舉變量的用法:
using System; namespace EnumApplication { class EnumProgram { enum Days { Sun, Mon, tue, Wed, thu, Fri, Sat }; static void Main(string[] args) { int WeekdayStart = (int)Days.Mon; int WeekdayEnd = (int)Days.Fri; Console.WriteLine("Monday: {0}", WeekdayStart); Console.WriteLine("Friday: {0}", WeekdayEnd); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
Monday: 1 Friday: 5
C# 多重繼承
C# 不支持多重繼承。但是,您可以使用接口來實現多重繼承。下面的程序演示了這點:
using System; namespace InheritanceApplication { class Shape { public void setWidth(int w) { width = w; } public void setHeight(int h) { height = h; } protected int width; protected int height; } // 基類 PaintCost public interface PaintCost { int getCost(int area); } // 派生類 class Rectangle : Shape, PaintCost { public int getArea() { return (width * height); } public int getCost(int area) { return area * 70; } } class RectangleTester { static void Main(string[] args) { Rectangle Rect = new Rectangle(); int area; Rect.setWidth(5); Rect.setHeight(7); area = Rect.getArea(); // 打印對象的面積 Console.WriteLine("總面積: {0}", Rect.getArea()); Console.WriteLine("油漆總成本: ${0}" , Rect.getCost(area)); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
總面積: 35 油漆總成本: $2450
C# 多態性
多態性意味着有多重形式。在面向對象編程範式中,多態性往往表現爲"一個接口,多個功能"。
多態性可以是靜態的或動態的。在靜態多態性中,函數的響應是在編譯時發生的。在動態多態性中,函數的響應是在運行時發生的。
靜態多態性
在編譯時,函數和對象的連接機制被稱爲早期綁定,也被稱爲靜態綁定。C# 提供了兩種技術來實現靜態多態性。分別爲:
- 函數重載
- 運算符重載
函數重載
您可以在同一個範圍內對相同的函數名有多個定義。函數的定義必須彼此不同,可以是參數列表中的參數類型不同,也可以是參數個數不同。不能重載只有返回類型不同的函數聲明。
下面的實例演示了幾個相同的函數 print(),用於打印不同的數據類型:
using System; namespace PolymorphismApplication { class Printdata { void print(int i) { Console.WriteLine("Printing int: {0}", i ); } void print(double f) { Console.WriteLine("Printing float: {0}" , f); } void print(string s) { Console.WriteLine("Printing string: {0}", s); } static void Main(string[] args) { Printdata p = new Printdata(); // 調用 print 來打印整數 p.print(5); // 調用 print 來打印浮點數 p.print(500.263); // 調用 print 來打印字符串 p.print("Hello C++"); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
Printing int: 5 Printing float: 500.263 Printing string: Hello C++
C# 運算符重載
您可以重定義或重載 C# 中內置的運算符。因此,程序員也可以使用用戶自定義類型的運算符。重載運算符是具有特殊名稱的函數,是通過關鍵字 operator 後跟運算符的符號來定義的。與其他函數一樣,重載運算符有返回類型和參數列表。
例如,請看下面的函數:
public static Box operator+ (Box b, Box c) { Box box = new Box(); box.length = b.length + c.length; box.breadth = b.breadth + c.breadth; box.height = b.height + c.height; return box; }
上面的函數爲用戶自定義的類 Box 實現了加法運算符(+)。它把兩個 Box 對象的屬性相加,並返回相加後的 Box 對象。
運算符重載的實現
下面的程序演示了完整的實現:
using System; namespace OperatorOvlApplication { class Box { private double length; // 長度 private double breadth; // 寬度 private double height; // 高度 public double getVolume() { return length * breadth * height; } public void setLength( double len ) { length = len; } public void setBreadth( double bre ) { breadth = bre; } public void setHeight( double hei ) { height = hei; } // 重載 + 運算符來把兩個 Box 對象相加 public static Box operator+ (Box b, Box c) { Box box = new Box(); box.length = b.length + c.length; box.breadth = b.breadth + c.breadth; box.height = b.height + c.height; return box; } } class Tester { static void Main(string[] args) { Box Box1 = new Box(); // 聲明 Box1,類型爲 Box Box Box2 = new Box(); // 聲明 Box2,類型爲 Box Box Box3 = new Box(); // 聲明 Box3,類型爲 Box double volume = 0.0; // 體積 // Box1 詳述 Box1.setLength(6.0); Box1.setBreadth(7.0); Box1.setHeight(5.0); // Box2 詳述 Box2.setLength(12.0); Box2.setBreadth(13.0); Box2.setHeight(10.0); // Box1 的體積 volume = Box1.getVolume(); Console.WriteLine("Box1 的體積: {0}", volume); // Box2 的體積 volume = Box2.getVolume(); Console.WriteLine("Box2 的體積: {0}", volume); // 把兩個對象相加 Box3 = Box1 + Box2; // Box3 的體積 volume = Box3.getVolume(); Console.WriteLine("Box3 的體積: {0}", volume); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
Box1 的體積: 210 Box2 的體積: 1560 Box3 的體積: 5400
可重載和不可重載運算符
下表描述了 C# 中運算符重載的能力:
運算符 | 描述 |
---|---|
+, -, !, ~, ++, -- | 這些一元運算符只有一個操作數,且可以被重載。 |
+, -, *, /, % | 這些二元運算符帶有兩個操作數,且可以被重載。 |
==, !=, <, >, <=, >= | 這些比較運算符可以被重載。 |
&&, || | 這些條件邏輯運算符不能被直接重載。 |
+=, -=, *=, /=, %= | 這些賦值運算符不能被重載。 |
=, ., ?:, ->, new, is, sizeof, typeof | 這些運算符不能被重載。 |
實例
針對上述討論,讓我們擴展上面的實例,重載更多的運算符:
using System; namespace OperatorOvlApplication { class Box { private double length; // 長度 private double breadth; // 寬度 private double height; // 高度 public double getVolume() { return length * breadth * height; } public void setLength( double len ) { length = len; } public void setBreadth( double bre ) { breadth = bre; } public void setHeight( double hei ) { height = hei; } // 重載 + 運算符來把兩個 Box 對象相加 public static Box operator+ (Box b, Box c) { Box box = new Box(); box.length = b.length + c.length; box.breadth = b.breadth + c.breadth; box.height = b.height + c.height; return box; } public static bool operator == (Box lhs, Box rhs) { bool status = false; if (lhs.length == rhs.length && lhs.height == rhs.height && lhs.breadth == rhs.breadth) { status = true; } return status; } public static bool operator !=(Box lhs, Box rhs) { bool status = false; if (lhs.length != rhs.length || lhs.height != rhs.height || lhs.breadth != rhs.breadth) { status = true; } return status; } public static bool operator <(Box lhs, Box rhs) { bool status = false; if (lhs.length < rhs.length && lhs.height < rhs.height && lhs.breadth < rhs.breadth) { status = true; } return status; } public static bool operator >(Box lhs, Box rhs) { bool status = false; if (lhs.length > rhs.length && lhs.height > rhs.height && lhs.breadth > rhs.breadth) { status = true; } return status; } public static bool operator <=(Box lhs, Box rhs) { bool status = false; if (lhs.length <= rhs.length && lhs.height <= rhs.height && lhs.breadth <= rhs.breadth) { status = true; } return status; } public static bool operator >=(Box lhs, Box rhs) { bool status = false; if (lhs.length >= rhs.length && lhs.height >= rhs.height && lhs.breadth >= rhs.breadth) { status = true; } return status; } public override string ToString() { return String.Format("({0}, {1}, {2})", length, breadth, height); } } class Tester { static void Main(string[] args) { Box Box1 = new Box(); // 聲明 Box1,類型爲 Box Box Box2 = new Box(); // 聲明 Box2,類型爲 Box Box Box3 = new Box(); // 聲明 Box3,類型爲 Box Box Box4 = new Box(); double volume = 0.0; // 體積 // Box1 詳述 Box1.setLength(6.0); Box1.setBreadth(7.0); Box1.setHeight(5.0); // Box2 詳述 Box2.setLength(12.0); Box2.setBreadth(13.0); Box2.setHeight(10.0); // 使用重載的 ToString() 顯示兩個盒子 Console.WriteLine("Box1: {0}", Box1.ToString()); Console.WriteLine("Box2: {0}", Box2.ToString()); // Box1 的體積 volume = Box1.getVolume(); Console.WriteLine("Box1 的體積: {0}", volume); // Box2 的體積 volume = Box2.getVolume(); Console.WriteLine("Box2 的體積: {0}", volume); // 把兩個對象相加 Box3 = Box1 + Box2; Console.WriteLine("Box3: {0}", Box3.ToString()); // Box3 的體積 volume = Box3.getVolume(); Console.WriteLine("Box3 的體積: {0}", volume); //comparing the boxes if (Box1 > Box2) Console.WriteLine("Box1 大於 Box2"); else Console.WriteLine("Box1 不大於 Box2"); if (Box1 < Box2) Console.WriteLine("Box1 小於 Box2"); else Console.WriteLine("Box1 不小於 Box2"); if (Box1 >= Box2) Console.WriteLine("Box1 大於等於 Box2"); else Console.WriteLine("Box1 不大於等於 Box2"); if (Box1 <= Box2) Console.WriteLine("Box1 小於等於 Box2"); else Console.WriteLine("Box1 不小於等於 Box2"); if (Box1 != Box2) Console.WriteLine("Box1 不等於 Box2"); else Console.WriteLine("Box1 等於 Box2"); Box4 = Box3; if (Box3 == Box4) Console.WriteLine("Box3 等於 Box4"); else Console.WriteLine("Box3 不等於 Box4"); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
Box1: (6, 7, 5) Box2: (12, 13, 10) Box1 的體積: 210 Box2 的體積: 1560 Box3: (18, 20, 15) Box3 的體積: 5400 Box1 不大於 Box2 Box1 小於 Box2 Box1 不大於等於 Box2 Box1 小於等於 Box2 Box1 不等於 Box2 Box3 等於 Box4
動態多態性
C# 允許您使用關鍵字 abstract 創建抽象類,用於提供接口的部分類的實現。當一個派生類繼承自該抽象類時,實現即完成。抽象類包含抽象方法,抽象方法可被派生類實現。派生類具有更專業的功能。
請注意,下面是有關抽象類的一些規則:
- 您不能創建一個抽象類的實例。
- 您不能在一個抽象類外部聲明一個抽象方法。
- 通過在類定義前面放置關鍵字 sealed,可以將類聲明爲密封類。當一個類被聲明爲 sealed 時,它不能被繼承。抽象類不能被聲明爲 sealed。
下面的程序演示了一個抽象類:
using System; namespace PolymorphismApplication { abstract class Shape { public abstract int area(); } class Rectangle: Shape { private int length; private int width; public Rectangle( int a=0, int b=0) { length = a; width = b; } public override int area () { Console.WriteLine("Rectangle 類的面積:"); return (width * length); } } class RectangleTester { static void Main(string[] args) { Rectangle r = new Rectangle(10, 7); double a = r.area(); Console.WriteLine("面積: {0}",a); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
Rectangle 類的面積: 面積: 70
當有一個定義在類中的函數需要在繼承類中實現時,可以使用虛方法。虛方法是使用關鍵字 virtual 聲明的。虛方法可以在不同的繼承類中有不同的實現。對虛方法的調用是在運行時發生的。
動態多態性是通過 抽象類 和 虛方法 實現的。
下面的程序演示了這點:
using System; namespace PolymorphismApplication { class Shape { protected int width, height; public Shape( int a=0, int b=0) { width = a; height = b; } public virtual int area() { Console.WriteLine("父類的面積:"); return 0; } } class Rectangle: Shape { public Rectangle( int a=0, int b=0): base(a, b) { } public override int area () { Console.WriteLine("Rectangle 類的面積:"); return (width * height); } } class Triangle: Shape { public Triangle(int a = 0, int b = 0): base(a, b) { } public override int area() { Console.WriteLine("Triangle 類的面積:"); return (width * height / 2); } } class Caller { public void CallArea(Shape sh) { int a; a = sh.area(); Console.WriteLine("面積: {0}", a); } } class Tester { static void Main(string[] args) { Caller c = new Caller(); Rectangle r = new Rectangle(10, 7); Triangle t = new Triangle(10, 5); c.CallArea(r); c.CallArea(t); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
Rectangle 類的面積: 面積:70 Triangle 類的面積: 面積:25
C# 接口(Interface)
接口定義了所有類繼承接口時應遵循的語法合同。接口定義了語法合同 "是什麼" 部分,派生類定義了語法合同 "怎麼做" 部分。
接口定義了屬性、方法和事件,這些都是接口的成員。接口只包含了成員的聲明。成員的定義是派生類的責任。接口提供了派生類應遵循的標準結構。
抽象類在某種程度上與接口類似,但是,它們大多隻是用在當只有少數方法由基類聲明由派生類實現時。
聲明接口
接口使用 interface 關鍵字聲明,它與類的聲明類似。接口聲明默認是 public 的。下面是一個接口聲明的實例:
public interface ITransactions { // 接口成員 void showTransaction(); double getAmount(); }
實例
下面的實例演示了上面接口的實現:
using System.Collections.Generic; using System.Linq; using System.Text; namespace InterfaceApplication { public interface ITransactions { // 接口成員 void showTransaction(); double getAmount(); } public class Transaction : ITransactions { private string tCode; private string date; private double amount; public Transaction() { tCode = " "; date = " "; amount = 0.0; } public Transaction(string c, string d, double a) { tCode = c; date = d; amount = a; } public double getAmount() { return amount; } public void showTransaction() { Console.WriteLine("Transaction: {0}", tCode); Console.WriteLine("Date: {0}", date); Console.WriteLine("Amount: {0}", getAmount()); } } class Tester { static void Main(string[] args) { Transaction t1 = new Transaction("001", "8/10/2012", 78900.00); Transaction t2 = new Transaction("002", "9/10/2012", 451900.00); t1.showTransaction(); t2.showTransaction(); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
Transaction: 001 Date: 8/10/2012 Amount: 78900 Transaction: 002 Date: 9/10/2012 Amount: 451900
C# 預處理器指令
預處理器指令指導編譯器在實際編譯開始之前對信息進行預處理。
所有的預處理器指令都是以 # 開始。且在一行上,只有空白字符可以出現在預處理器指令之前。預處理器指令不是語句,所以它們不以分號(;)結束。
C# 編譯器沒有一個單獨的預處理器,但是,指令被處理時就像是有一個單獨的預處理器一樣。在 C# 中,預處理器指令用於在條件編譯中起作用。與 C 和 C++ 不同指令不用,它們不是用來創建宏。一個預處理器指令必須是該行上的唯一指令。
C# 預處理器指令列表
下表列出了 C# 中可用的預處理器指令:
預處理器指令 | 描述 |
---|---|
#define | 它用於定義一系列成爲符號的字符。 |
#undef | 它用於取消定義符號。 |
#if | 它用於測試符號是否爲真。 |
#else | 它用於創建複合條件指令,與 #if 一起使用。 |
#elif | 它用於創建複合條件指令。 |
#endif | 指定一個條件指令的結束。 |
#line | 它可以讓您修改編譯器的行數以及(可選地)輸出錯誤和警告的文件名。 |
#error | 它允許從代碼的指定位置生成一個錯誤。 |
#warning | 它允許從代碼的指定位置生成一級警告。 |
#region | 它可以讓您在使用 Visual Studio Code Editor 的大綱特性時,指定一個可展開或摺疊的代碼塊。 |
#endregion | 它標識着 #region 塊的結束。 |
#define 預處理器
#define 預處理器指令創建符號常量。
#define 允許您定義一個符號,這樣,通過使用符號作爲傳遞給 #if 指令的表達式,表達式將返回 true。它的語法如下:
#define symbol
下面的程序說明了這點:
#define PI using System; namespace PreprocessorDAppl { class Program { static void Main(string[] args) { #if (PI) Console.WriteLine("PI is defined"); #else Console.WriteLine("PI is not defined"); #endif Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
PI is defined
條件指令
您可以使用 #if 指令來創建一個條件指令。條件指令用於測試符號是否爲真。如果爲真,編譯器會執行 #if 和下一個指令之間的代碼。
條件指令的語法:
#if symbol [operator symbol]...
其中,symbol 是要測試的符號名稱。您也可以使用 true 和 false,或在符號前放置否定運算符。
運算符符號是用於評價符號的運算符。可以運算符可以是下列運算符之一:
- == (equality)
- != (inequality)
- && (and)
- || (or)
您也可以用括號把符號和運算符進行分組。條件指令用於在調試版本或編譯指定配置時編譯代碼。一個以 #if 指令開始的條件指令,必須顯示地以一個 #endif 指令終止。
下面的程序演示了條件指令的用法:
#define DEBUG #define VC_V10 using System; public class TestClass { public static void Main() { #if (DEBUG && !VC_V10) Console.WriteLine("DEBUG is defined"); #elif (!DEBUG && VC_V10) Console.WriteLine("VC_V10 is defined"); #elif (DEBUG && VC_V10) Console.WriteLine("DEBUG and VC_V10 are defined"); #else Console.WriteLine("DEBUG and VC_V10 are not defined"); #endif Console.ReadKey(); } }
當上面的代碼被編譯和執行時,它會產生下列結果:
DEBUG and VC_V10 are defined
C# 正則表達式
正則表達式 是一種匹配輸入文本的模式。.Net 框架提供了允許這種匹配的正則表達式引擎。模式由一個或多個字符、運算符和結構組成。
定義正則表達式
下面列出了用於定義正則表達式的各種類別的字符、運算符和結構。
- 字符轉義
- 字符類
- 定位點
- 分組構造
- 限定符
- 反向引用構造
- 備用構造
- 替換
- 雜項構造
字符轉義
正則表達式中的反斜槓字符(\)指示其後跟的字符是特殊字符,或應按原義解釋該字符。
下表列出了轉義字符:
轉義字符 | 描述 | 模式 | 匹配 |
---|---|---|---|
\a | 與報警 (bell) 符 \u0007 匹配。 | \a | "Warning!" + '\u0007' 中的 "\u0007" |
\b | 在字符類中,與退格鍵 \u0008 匹配。 | [\b]{3,} | "\b\b\b\b" 中的 "\b\b\b\b" |
\t | 與製表符 \u0009 匹配。 | (\w+)\t | "Name\tAddr\t" 中的 "Name\t" 和 "Addr\t" |
\r | 與回車符 \u000D 匹配。(\r 與換行符 \n 不是等效的。) | \r\n(\w+) | "\r\Hello\nWorld." 中的 "\r\nHello" |
\v | 與垂直製表符 \u000B 匹配。 | [\v]{2,} | "\v\v\v" 中的 "\v\v\v" |
\f | 與換頁符 \u000C 匹配。 | [\f]{2,} | "\f\f\f" 中的 "\f\f\f" |
\n | 與換行符 \u000A 匹配。 | \r\n(\w+) | "\r\Hello\nWorld." 中的 "\r\nHello" |
\e | 與轉義符 \u001B 匹配。 | \e | "\x001B" 中的 "\x001B" |
\ nnn | 使用八進制表示形式指定一個字符(nnn 由二到三位數字組成)。 | \w\040\w | "a bc d" 中的 "a b" 和 "c d" |
\x nn | 使用十六進制表示形式指定字符(nn 恰好由兩位數字組成)。 | \w\x20\w | "a bc d" 中的 "a b" 和 "c d" |
\c X \c x | 匹配 X 或 x 指定的 ASCII 控件字符,其中 X 或 x 是控件字符的字母。 | \cC | "\x0003" 中的 "\x0003" (Ctrl-C) |
\u nnnn | 使用十六進制表示形式匹配一個 Unicode 字符(由 nnnn 表示的四位數)。 | \w\u0020\w | "a bc d" 中的 "a b" 和 "c d" |
\ | 在後面帶有不識別的轉義字符時,與該字符匹配。 | \d+[\+-x\*]\d+\d+[\+-x\*\d+ | "(2+2) * 3*9" 中的 "2+2" 和 "3*9" |
字符類
字符類與一組字符中的任何一個字符匹配。
下表列出了字符類:
字符類 | 描述 | 模式 | 匹配 |
---|---|---|---|
[character_group] | 匹配 character_group 中的任何單個字符。 默認情況下,匹配區分大小寫。 | [mn] | "mat" 中的 "m","moon" 中的 "m" 和 "n" |
[^character_group] | 非:與不在 character_group 中的任何單個字符匹配。 默認情況下,character_group 中的字符區分大小寫。 | [^aei] | "avail" 中的 "v" 和 "l" |
[ first - last ] | 字符範圍:與從 first 到 last 的範圍中的任何單個字符匹配。 | (\w+)\t | "Name\tAddr\t" 中的 "Name\t" 和 "Addr\t" |
. |
通配符:與除 \n 之外的任何單個字符匹配。 若要匹配原意句點字符(. 或 \u002E),您必須在該字符前面加上轉義符 (\.)。 |
a.e | "have" 中的 "ave", "mate" 中的 "ate" |
\p{ name } | 與 name 指定的 Unicode 通用類別或命名塊中的任何單個字符匹配。 | \p{Lu} | "City Lights" 中的 "C" 和 "L" |
\P{ name } | 與不在 name 指定的 Unicode 通用類別或命名塊中的任何單個字符匹配。 | \P{Lu} | "City" 中的 "i"、 "t" 和 "y" |
\w | 與任何單詞字符匹配。 | \w | "Room#1" 中的 "R"、 "o"、 "m" 和 "1" |
\W | 與任何非單詞字符匹配。 | \W | "Room#1" 中的 "#" |
\s | 與任何空白字符匹配。 | \w\s | "ID A1.3" 中的 "D " |
\S | 與任何非空白字符匹配。 | \s\S | "int __ctr" 中的 " _" |
\d | 與任何十進制數字匹配。 | \d | "4 = IV" 中的 "4" |
\D | 匹配不是十進制數的任意字符。 | \D | "4 = IV" 中的 " "、 "="、 " "、 "I" 和 "V" |
定位點
定位點或原子零寬度斷言會使匹配成功或失敗,具體取決於字符串中的當前位置,但它們不會使引擎在字符串中前進或使用字符。
下表列出了定位點:
斷言 | 描述 | 模式 | 匹配 |
---|---|---|---|
^ | 匹配必須從字符串或一行的開頭開始。 | ^\d{3} | "567-777-" 中的 "567" |
$ | 匹配必須出現在字符串的末尾或出現在行或字符串末尾的 \n 之前。 | -\d{4}$ | "8-12-2012" 中的 "-2012" |
\A | 匹配必須出現在字符串的開頭。 | \A\w{3} | "Code-007-" 中的 "Code" |
\Z | 匹配必須出現在字符串的末尾或出現在字符串末尾的\n 之前。 | -\d{3}\Z | "Bond-901-007" 中的 "-007" |
\z | 匹配必須出現在字符串的末尾。 | -\d{3}\z | "-901-333" 中的 "-333" |
\G | 匹配必須出現在上一個匹配結束的地方。 | \\G\(\d\) | "(1)(3)(5)[7](9)" 中的 "(1)"、 "(3)" 和 "(5)" |
\b | 匹配必須出現在 \w(字母數字)和 \W(非字母數字)字符之間的邊界上。 | \w | "Room#1" 中的 "R"、 "o"、 "m" 和 "1" |
\B | 匹配不得出現在 \b 邊界上。 | \Bend\w*\b | "end sends endure lender" 中的 "ends" 和 "ender" |
分組構造
分組構造描述了正則表達式的子表達式,通常用於捕獲輸入字符串的子字符串。
下表列出了分組構造:
分組構造 | 描述 | 模式 | 匹配 |
---|---|---|---|
( subexpression ) | 捕獲匹配的子表達式並將其分配到一個從零開始的序號中。 | (\w)\1 | "deep" 中的 "ee" |
(?< name >subexpression) | 將匹配的子表達式捕獲到一個命名組中。 | (?< double>\w)\k< double> | "deep" 中的 "ee" |
(?< name1 -name2 >subexpression) | 定義平衡組定義。 | (((?'Open'\()[^\(\)]*)+((?'Close-Open'\))[^\(\)]*)+)*(?(Open)(?!))$ | "3+2^((1-3)*(3-1))" 中的 "((1-3)*(3-1))" |
(?: subexpression) | 定義非捕獲組。 | Write(?:Line)? | "Console.WriteLine()" 中的 "WriteLine" |
(?imnsx-imnsx:subexpression) | 應用或禁用 subexpression 中指定的選項。 | A\d{2}(?i:\w+)\b | "A12xl A12XL a12xl" 中的 "A12xl" 和 "A12XL" |
(?= subexpression) | 零寬度正預測先行斷言。 | \w+(?=\.) | "He is. The dog ran. The sun is out." 中的 "is"、 "ran" 和 "out" |
(?! subexpression) | 零寬度負預測先行斷言。 | \b(?!un)\w+\b | "unsure sure unity used" 中的 "sure" 和 "used" |
(?< =subexpression) | 零寬度正回顧後發斷言。 | (?< =19)\d{2}\b | "1851 1999 1950 1905 2003" 中的 "51" 和 "03" |
(?< ! subexpression) | 零寬度負回顧後發斷言。 | (?< !19)\d{2}\b | "end sends endure lender" 中的 "ends" 和 "ender" |
(?> subexpression) | 非回溯(也稱爲"貪婪")子表達式。 | [13579](?>A+B+) | "1ABB 3ABBC 5AB 5AC" 中的 "1ABB"、 "3ABB" 和 "5AB" |
限定符
限定符指定在輸入字符串中必須存在上一個元素(可以是字符、組或字符類)的多少個實例才能出現匹配項。 限定符包括下表中列出的語言元素。
下表列出了限定符:
限定符 | 描述 | 模式 | 匹配 |
---|---|---|---|
* | 匹配上一個元素零次或多次。 | \d*\.\d | ".0"、 "19.9"、 "219.9" |
+ | 匹配上一個元素一次或多次。 | "be+" | "been" 中的 "bee", "bent" 中的 "be" |
? | 匹配上一個元素零次或一次。 | "rai?n" | "ran"、 "rain" |
{ n } | 匹配上一個元素恰好 n 次。 | ",\d{3}" | "1,043.6" 中的 ",043", "9,876,543,210" 中的 ",876"、 ",543" 和 ",210" |
{ n ,} | 匹配上一個元素至少 n 次。 | "\d{2,}" | "166"、 "29"、 "1930" |
{ n , m } | 匹配上一個元素至少 n 次,但不多於 m 次。 | "\d{3,5}" | "166", "17668", "193024" 中的 "19302" |
*? | 匹配上一個元素零次或多次,但次數儘可能少。 | \d*?\.\d | ".0"、 "19.9"、 "219.9" |
+? | 匹配上一個元素一次或多次,但次數儘可能少。 | "be+?" | "been" 中的 "be", "bent" 中的 "be" |
?? | 匹配上一個元素零次或一次,但次數儘可能少。 | "rai??n" | "ran"、 "rain" |
{ n }? | 匹配前導元素恰好 n 次。 | ",\d{3}?" | "1,043.6" 中的 ",043", "9,876,543,210" 中的 ",876"、 ",543" 和 ",210" |
{ n ,}? | 匹配上一個元素至少 n 次,但次數儘可能少。 | "\d{2,}?" | "166"、 "29" 和 "1930" |
{ n , m }? | 匹配上一個元素的次數介於 n 和 m 之間,但次數儘可能少。 | "\d{3,5}?" | "166", "17668", "193024" 中的 "193" 和 "024" |
反向引用構造
反向引用允許在同一正則表達式中隨後標識以前匹配的子表達式。
下表列出了反向引用構造:
反向引用構造 | 描述 | 模式 | 匹配 |
---|---|---|---|
\ number | 反向引用。 匹配編號子表達式的值。 | (\w)\1 | "seek" 中的 "ee" |
\k< name > | 命名反向引用。 匹配命名表達式的值。 | (?< char>\w)\k< char> | "seek" 中的 "ee" |
備用構造
備用構造用於修改正則表達式以啓用 either/or 匹配。
下表列出了備用構造:
備用構造 | 描述 | 模式 | 匹配 |
---|---|---|---|
| | 匹配以豎線 (|) 字符分隔的任何一個元素。 | th(e|is|at) | "this is the day. " 中的 "the" 和 "this" |
(?( expression )yes | no ) | 如果正則表達式模式由 expression 匹配指定,則匹配 yes;否則匹配可選的 no部分。 expression 被解釋爲零寬度斷言。 | (?(A)A\d{2}\b|\b\d{3}\b) | "A10 C103 910" 中的 "A10" 和 "910" |
(?( name )yes | no ) | 如果 name 或已命名或已編號的捕獲組具有匹配,則匹配 yes;否則匹配可選的 no。 | (?< quoted>")?(?(quoted).+?"|\S+\s) | "Dogs.jpg "Yiska playing.jpg"" 中的 Dogs.jpg 和 "Yiska playing.jpg" |
替換
替換是替換模式中使用的正則表達式。
下表列出了用於替換的字符:
字符 | 描述 | 模式 | 替換模式 | 輸入字符串 | 結果字符串 |
---|---|---|---|---|---|
$number | 替換按組 number 匹配的子字符串。 | \b(\w+)(\s)(\w+)\b | $3$2$1 | "one two" | "two one" |
${name} | 替換按命名組 name 匹配的子字符串。 | \b(?< word1>\w+)(\s)(?< word2>\w+)\b | ${word2} ${word1} | "one two" | "two one" |
$$ | 替換字符"$"。 | \b(\d+)\s?USD | $$$1 | "103 USD" | "$103" |
$& | 替換整個匹配項的一個副本。 | (\$*(\d*(\.+\d+)?){1}) | **$& | "$1.30" | "**$1.30**" |
$` | 替換匹配前的輸入字符串的所有文本。 | B+ | $` | "AABBCC" | "AAAACC" |
$' | 替換匹配後的輸入字符串的所有文本。 | B+ | $' | "AABBCC" | "AACCCC" |
$+ | 替換最後捕獲的組。 | B+(C+) | $+ | "AABBCCDD" | AACCDD |
$_ | 替換整個輸入字符串。 | B+ | $_ | "AABBCC" | "AAAABBCCCC" |
雜項構造
下表列出了各種雜項構造:
構造 | 描述 | 實例 |
---|---|---|
(?imnsx-imnsx) | 在模式中間對諸如不區分大小寫這樣的選項進行設置或禁用。 | \bA(?i)b\w+\b 匹配 "ABA Able Act" 中的 "ABA" 和 "Able" |
(?#comment) | 內聯註釋。該註釋在第一個右括號處終止。 | \bA(?#Matches words starting with A)\w+\b |
# [to end of line] | X 模式註釋。 該註釋以非轉義的 # 開頭,並繼續到行的結尾。 | (?x)\bA\w+\b#Matches words starting with A |
Regex 類
Regex 類用於表示一個正則表達式。
下表列出了 Regex 類中一些常用的方法:
序號 | 方法 & 描述 |
---|---|
1 |
public bool IsMatch( string input ) 指示 Regex 構造函數中指定的正則表達式是否在指定的輸入字符串中找到匹配項。 |
2 |
public bool IsMatch( string input, int startat ) 指示 Regex 構造函數中指定的正則表達式是否在指定的輸入字符串中找到匹配項,從字符串中指定的開始位置開始。 |
3 |
public static bool IsMatch( string input, string pattern ) 指示指定的正則表達式是否在指定的輸入字符串中找到匹配項。 |
4 |
public MatchCollection Matches( string input ) 在指定的輸入字符串中搜索正則表達式的所有匹配項。 |
5 |
public string Replace( string input, string replacement ) 在指定的輸入字符串中,把所有匹配正則表達式模式的所有匹配的字符串替換爲指定的替換字符串。 |
6 |
public string[] Split( string input ) 把輸入字符串分割爲子字符串數組,根據在 Regex 構造函數中指定的正則表達式模式定義的位置進行分割。 |
如需瞭解 Regex 類的完整的屬性列表,請參閱微軟的 C# 文檔。
實例 1
下面的實例匹配了以 'S' 開頭的單詞:
using System; using System.Text.RegularExpressions; namespace RegExApplication { class Program { private static void showMatch(string text, string expr) { Console.WriteLine("The Expression: " + expr); MatchCollection mc = Regex.Matches(text, expr); foreach (Match m in mc) { Console.WriteLine(m); } } static void Main(string[] args) { string str = "A Thousand Splendid Suns"; Console.WriteLine("Matching words that start with 'S': "); showMatch(str, @"\bS\S*"); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
Matching words that start with 'S': The Expression: \bS\S* Splendid Suns
實例 2
下面的實例匹配了以 'm' 開頭以 'e' 結尾的單詞:
using System; using System.Text.RegularExpressions; namespace RegExApplication { class Program { private static void showMatch(string text, string expr) { Console.WriteLine("The Expression: " + expr); MatchCollection mc = Regex.Matches(text, expr); foreach (Match m in mc) { Console.WriteLine(m); } } static void Main(string[] args) { string str = "make maze and manage to measure it"; Console.WriteLine("Matching words start with 'm' and ends with 'e':"); showMatch(str, @"\bm\S*e\b"); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
Matching words start with 'm' and ends with 'e': The Expression: \bm\S*e\b make maze manage measure
實例 3
下面的實例替換掉多餘的空格:
using System; using System.Text.RegularExpressions; namespace RegExApplication { class Program { static void Main(string[] args) { string input = "Hello World "; string pattern = "\\s+"; string replacement = " "; Regex rgx = new Regex(pattern); string result = rgx.Replace(input, replacement); Console.WriteLine("Original String: {0}", input); Console.WriteLine("Replacement String: {0}", result); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
Original String: Hello World Replacement String: Hello World
C# 異常處理
異常是在程序執行期間出現的問題。C# 中的異常是對程序運行時出現的特殊情況的一種響應,比如嘗試除以零。
異常提供了一種把程序控制權從某個部分轉移到另一個部分的方式。C# 異常處理時建立在四個關鍵詞之上的:try、catch、finally 和 throw。
- try:一個 try 塊標識了一個將被激活的特定的異常的代碼塊。後跟一個或多個 catch 塊。
- catch:程序通過異常處理程序捕獲異常。catch 關鍵字表示異常的捕獲。
- finally:finally 塊用於執行給定的語句,不管異常是否被拋出都會執行。例如,如果您打開一個文件,不管是否出現異常文件都要被關閉。
- throw:當問題出現時,程序拋出一個異常。使用 throw 關鍵字來完成。
語法
假設一個塊將出現異常,一個方法使用 try 和 catch 關鍵字捕獲異常。try/catch 塊內的代碼爲受保護的代碼,使用 try/catch 語法如下所示:
try { // 引起異常的語句 } catch( ExceptionName e1 ) { // 錯誤處理代碼 } catch( ExceptionName e2 ) { // 錯誤處理代碼 } catch( ExceptionName eN ) { // 錯誤處理代碼 } finally { // 要執行的語句 }
您可以列出多個 catch 語句捕獲不同類型的異常,以防 try 塊在不同的情況下生成多個異常。
C# 中的異常類
C# 異常是使用類來表示的。C# 中的異常類主要是直接或間接地派生於 System.Exception 類。System.ApplicationException 和System.SystemException 類是派生於 System.Exception 類的異常類。
System.ApplicationException 類支持由應用程序生成的異常。所以程序員定義的異常都應派生自該類。
System.SystemException 類是所有預定義的系統異常的基類。
下表列出了一些派生自 Sytem.SystemException 類的預定義的異常類:
異常類 | 描述 |
---|---|
System.IO.IOException | 處理 I/O 錯誤。 |
System.IndexOutOfRangeException | 處理當方法指向超出範圍的數組索引時生成的錯誤。 |
System.ArrayTypeMismatchException | 處理當數組類型不匹配時生成的錯誤。 |
System.NullReferenceException | 處理當依從一個空對象時生成的錯誤。 |
System.DivideByZeroException | 處理當除以零時生成的錯誤。 |
System.InvalidCastException | 處理在類型轉換期間生成的錯誤。 |
System.OutOfMemoryException | 處理空閒內存不足生成的錯誤。 |
System.StackOverflowException | 處理棧溢出生成的錯誤。 |
異常處理
C# 以 try 和 catch 塊的形式提供了一種結構化的異常處理方案。使用這些塊,把核心程序語句與錯誤處理語句分離開。
這些錯誤處理塊是使用 try、catch 和 finally 關鍵字實現的。下面是一個當除以零時拋出異常的實例:
using System; namespace ErrorHandlingApplication { class DivNumbers { int result; DivNumbers() { result = 0; } public void division(int num1, int num2) { try { result = num1 / num2; } catch (DivideByZeroException e) { Console.WriteLine("Exception caught: {0}", e); } finally { Console.WriteLine("Result: {0}", result); } } static void Main(string[] args) { DivNumbers d = new DivNumbers(); d.division(25, 0); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
Exception caught: System.DivideByZeroException: Attempted to divide by zero. at ... Result: 0
創建用戶自定義異常
您也可以定義自己的異常。用戶自定義的異常類是派生自 ApplicationException 類。下面的實例演示了這點:
using System; namespace UserDefinedException { class TestTemperature { static void Main(string[] args) { Temperature temp = new Temperature(); try { temp.showTemp(); } catch(TempIsZeroException e) { Console.WriteLine("TempIsZeroException: {0}", e.Message); } Console.ReadKey(); } } } public class TempIsZeroException: ApplicationException { public TempIsZeroException(string message): base(message) { } } public class Temperature { int temperature = 0; public void showTemp() { if(temperature == 0) { throw (new TempIsZeroException("Zero Temperature found")); } else { Console.WriteLine("Temperature: {0}", temperature); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
TempIsZeroException: Zero Temperature found
拋出對象
如果異常是直接或間接派生自 System.Exception 類,您可以拋出一個對象。您可以在 catch 塊中使用 throw 語句來拋出當前的對象,如下所示:
Catch(Exception e) { ... Throw e }
C# 文件的輸入與輸出
一個 文件 是一個存儲在磁盤中帶有指定名稱和目錄路徑的數據集合。當打開文件進行讀寫時,它變成一個 流。
從根本上說,流是通過通信路徑傳遞的字節序列。有兩個主要的流:輸入流 和 輸出流。輸入流用於從文件讀取數據(讀操作),輸出流用於向文件寫入數據(寫操作)。
C# I/O 類
System.IO 命名空間有各種不同的類,用於執行各種文件操作,如創建和刪除文件、讀取或寫入文件,關閉文件等。
下表列出了一些 System.IO 命名空間中常用的非抽象類:
I/O 類 | 描述 |
---|---|
BinaryReader | 從二進制流讀取原始數據。 |
BinaryWriter | 以二進制格式寫入原始數據。 |
BufferedStream | 字節流的臨時存儲。 |
Directory | 有助於操作目錄結構。 |
DirectoryInfo | 用於對目錄執行操作。 |
DriveInfo | 提供驅動器的信息。 |
File | 有助於處理文件。 |
FileInfo | 用於對文件執行操作。 |
FileStream | 用於文件中任何位置的讀寫。 |
MemoryStream | 用於隨機訪問存儲在內存中的數據流。 |
Path | 對路徑信息執行操作。 |
StreamReader | 用於從字節流中讀取字符。 |
StreamWriter | 用於向一個流中寫入字符。 |
StringReader | 用於讀取字符串緩衝區。 |
StringWriter | 用於寫入字符串緩衝區。 |
FileStream 類
System.IO 命名空間中的 FileStream 類有助於文件的讀寫與關閉。該類派生自抽象類 Stream。
您需要創建一個 FileStream 對象來創建一個新的文件,或打開一個已有的文件。創建 FileStream 對象的語法如下:
FileStream <object_name> = new FileStream( <file_name>, <FileMode Enumerator>, <FileAccess Enumerator>, <FileShare Enumerator>);
例如,創建一個 FileStream 對象 F 來讀取名爲 sample.txt 的文件:
FileStream F = new FileStream("sample.txt", FileMode.Open, FileAccess.Read, FileShare.Read);
參數 | 描述 |
---|---|
FileMode |
FileMode 枚舉定義了各種打開文件的方法。FileMode 枚舉的成員有:
|
FileAccess |
FileAccess 枚舉的成員有:Read、ReadWrite 和 Write。 |
FileShare |
FileShare 枚舉的成員有:
|
實例
下面的程序演示了 FileStream 類的用法:
using System; using System.IO; namespace FileIOApplication { class Program { static void Main(string[] args) { FileStream F = new FileStream("test.dat", FileMode.OpenOrCreate, FileAccess.ReadWrite); for (int i = 1; i <= 20; i++) { F.WriteByte((byte)i); } F.Position = 0; for (int i = 0; i <= 20; i++) { Console.Write(F.ReadByte() + " "); } F.Close(); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -1
C# 高級文件操作
上面的實例演示了 C# 中簡單的文件操作。但是,要充分利用 C# System.IO 類的強大功能,您需要知道這些類常用的屬性和方法。
在下面的章節中,我們將討論這些類和它們執行的操作。請單擊鏈接詳細瞭解各個部分的知識:
主題 | 描述 |
---|---|
文本文件的讀寫 | 它涉及到文本文件的讀寫。StreamReader 和 StreamWriter 類有助於完成文本文件的讀寫。 |
二進制文件的讀寫 | 它涉及到二進制文件的讀寫。BinaryReader 和 BinaryWriter 類有助於完成二進制文件的讀寫。 |
Windows 文件系統的操作 | 它讓 C# 程序員能夠瀏覽並定位 Windows 文件和目錄。 |