在一小時內學會 C#。使用例程,簡單卻完整的探索 C# 語言的構造和特色。本文特別適合有 C++ 基礎卻沒有太多精力學習 C# 的讀者。編程
關於做者數組
Aisha Ikram安全
我如今在英國一家軟件公司任技術帶頭人。我是計算機科 學的碩士。我主要使用 .NET 1.1/2.0, C#, VB.NET, ASP.NET, VC++ 6, MFC, ATL, COM/DCOM, SQL Server 2000/2005等。最近我在學習 .NET 3.x 的所有內容。個人免費源代碼和文章網站是 http://aishai.netfirms.com/職業:團隊帶頭人位置:英國編程語言
簡介ide
C# 是一種具備 C++ 特性,Java 樣式及 BASIC 快速建模特性的編程語言。若是你已經知曉 C++ 語言,本文將在不到一小時的時間內帶你快速瀏覽 C# 的語法。若是熟悉 Java 語言,Java 的編程結構、打包和垃圾回收的概念確定對你快速學習 C# 大有幫助。因此我在討論 C# 語言構造的時候會假設你知道 C++。函數
本文經過一系列例程以簡短但全面的方式討論了 C# 語言構造和特性,因此你僅需略覽代碼片刻,便可瞭解其概念。學習
注意:本文不是爲 C# 宗師而寫。有不少初學者的 C# 文章,這只是其中之一。網站
接下來關於 C# 的討論主題:ui
編程結構 命名空間 數據類型 變量 運算符與表達式 枚舉 語句 類與結構 修飾符 屬性 接口 函數參數 數組 索引器 裝箱與拆箱 委託 繼承與多態如下主題不會進行討論:this
C++ 與 C# 的共同點 諸如垃圾回收、線程、文件處理等概念 數據類型轉換 異常處理 .NET 庫
編程結構
和 C++ 同樣,C# 是大小寫敏感的。半角分號(;)是語句分隔符。和 C++ 有所區別的是,C# 中沒有單獨的聲明(頭)和實現(CPP)文件。全部代碼(類聲明和實現)都放在擴展名爲 cs 的單一文件中。
看看 C# 中的 Hello World 程序。複製內容到剪貼板
[8][9][10] ...
代碼:using System;
namespace MyNameSpace
{
class HelloWorld
{ static void Main(string[] args) { Console.WriteLine ("Hello World"); }}
}
C# 中全部內容都打包在類中,而全部的類又打包在命名空間中(正如文件存與文件夾中)。和 C++ 同樣,有一個主函數做爲你程序的入口點。C++ 的主函數名爲 main,而 C# 中是大寫 M 打頭的 Main。
類塊或結構定義以後沒有必要再加一個半角分號。C++ 中是這樣,但 C# 不要求。
命名空間
每一個類都打包於一個命名空間。命名空間的概念和 C++ 徹底同樣,但咱們在 C# 中比在 C++ 中更加頻繁的使用命名空間。你能夠用點(.)定界符訪問命名空間中的類。上面的 Hello World 程序中,MyNameSpace 是其命名空間。
現 在思考當你要從其餘命名空間的類中訪問 HelloWorld 類。複製內容到剪貼板代碼:using System;namespace AnotherNameSpace{class AnotherClass { public void Func() { Console.WriteLine ("Hello World"); } }}如今在你的 HelloWorld 類中你能夠這樣訪問:複製內容到剪貼板代碼:using System;using AnotherNameSpace; // 你能夠增長這條語句namespace MyNameSpace{class HelloWorld{ static void Main(string[] args){AnotherClass obj = new AnotherClass();obj.Func(); }}}在 .NET 庫中,System 是包含其餘命名空間的頂層命名空間。默認狀況下存在一個全局命名空間,因此在命名空間外定義的類直接進到此全局命名空間中,於是你能夠不用定界符訪問此 類。你一樣能夠定義嵌套命名空間。
Using
#include 指示符被後跟命名空間名的 using 關鍵字代替了。正如上面的 using System。System 是最基層的命名空間,全部其餘命名空間和類都包含於其中。System 命名空間中全部對象的基類是 Object。
[8][9][10] ...
變量
除了如下差別,C# 中的變量幾乎和 C++ 中同樣:
1. C# 中(不一樣於 C++)的變量,老是須要你在訪問它們前先進行初始化,不然你將遇到編譯時錯誤。故而,不可能訪問未初始化的變量。2. 你不能在 C# 中訪問一個「掛起」指針。3. 超出數組邊界的表達式索引值一樣不可訪問。4. C# 中沒有全局變量或全局函數,取而代之的是經過靜態函數和靜態變量完成的。
數據類型全部 C# 的類型都是從 object 類繼承的。有兩種數據類型:
1. 基本/內建類型2. 用戶定義類型
如下是 C# 內建類型的列表:
類 型 字節 描述byte 1 unsigned byte sbyte 1 signed byte short 2 signed short ushort 2 unsigned short int 4 signed integer uint 4 unsigned integer long 8 signed long ulong 8 unsigned long float 4 floating point number double 8 double precision number decimal 8 fixed precision number string - Unicode string char - Unicode char bool true, false boolean
注意:C# 的類型範圍和 C++ 不一樣。例如:long 在 C++ 中是 4 字節而在 C# 中是 8 字節。bool 和 string 類型均和 C++ 不一樣。bool 僅接受真、假而非任意整數。
用戶定義類型文件包含:
1. 類 (class)2. 結構(struct)3. 接口(interface)
如下類型繼承時均分配內存:
1. 值類型2. 參考類型
值類型
值類型是在堆棧中分配的數據類型。它們包括了:
? 除字符串,全部基本和內建類型? 結構? 枚舉類型
[8][9][10] ...
引用類型
引用類型在堆(heap)中分配內存且當其再也不使用時,將自動進行垃圾清理。和 C++ 要求用戶顯示建立 delete 運算符不同,它們使用新運算符建立,且沒有 delete 運算符。在 C# 中它們自動由垃圾回收系統回收。
引用類型包括:
? 類? 接口? 集合類型如數組? 字符串
枚舉
C# 中的枚舉和 C++ 徹底同樣。經過關鍵字 enum 定義。
例子:複製內容到剪貼板代碼:enum Weekdays{ Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday}類與結構
除了內存分配的不一樣外,類和結構就和 C++ 中的狀況同樣。類的對象在堆中分配,並使用 new 關鍵字建立。而結構是在棧(stack)中進行分配。C# 中的結構屬於輕量級快速數據類型。當須要大型數據類型時,你應該建立類。
例 子:複製內容到剪貼板代碼:struct Date{ int day; int month; int year;} class Date{ int day; int month; int year; string weekday;string monthName; public int GetDay() {return day; } public int GetMonth(){return month; } public int GetYear(){return year; } public void SetDay(int Day){day = Day ; } public void SetMonth(int Month) { month = Month; } public void SetYear(int Year) {year = Year; } public bool IsLeapYear() { return (year/4 == 0); } public void SetDate (int day, int month, int year) { } ...}
[8][9][10] ...
屬性
若是你熟悉 C++ 面向對象的方法,你必定對屬性有本身的認識。對 C++ 來講,前面例子中 Date 類的屬性就是 day、month 和 year,而你添加了 Get 和 Set 方法。C# 提供了一種更加便捷、簡單而又直接的屬性訪問方式。
因此上面的類應該寫成這樣:複製內容到剪貼板代碼:using System;class Date{ public int Day{ get { return day;} set { day = value;} } int day;
public int Month{ get {return month; } set { month = value;} } int month;
public int Year{ get {return year; } set { year = value; } } int year;
public bool IsLeapYear(int year) { return year%4== 0 ? true: false;} public void SetDate (int day, int month, int year) { this.day = day; this.month = month; this.year = year; }}這裏是你 get 和 set 屬性的方法:複製內容到剪貼板代碼:class User{ public static void Main() {Date date = new Date();date.Day = 27;date.Month = 6;date.Year = 2003; Console.WriteLine ("Date: {0}/{1}/{2}", date.Day, date.Month, date.Year); }}修飾符
你必須知道 C++ 中經常使用的 public、private 和 protected 修飾符。我將在這裏討論一些 C# 引入的新的修飾符。
readonly
readonly 修飾符僅用於修飾類的數據成員。正如其名字說的,一旦它們已經進行了寫操做、直接初始化或在構造函數中對其進行了賦值,readonly 數據成員就只能對其進行讀取。readonly 和 const 數據成員不一樣之處在於 const 要求你在聲明時進行直接初始化。看下面的例程:複製內容到剪貼板
[8][9][10] ...
代碼:class MyClass{ const int constInt = 100; //直接進行 readonly int myInt = 5; //直接進行 readonly int myInt2;public MyClass() { myInt2 = 8; //間接進行 } public Func() { myInt = 7; //非法 Console.WriteLine(myInt2.ToString()); }}sealed
帶有 sealed 修飾符的類不容許你從它繼承任何類。因此若是你不想一個類被繼承,你能夠對該類使用 sealed 關鍵字。複製內容到剪貼板代碼:sealed class CanNotbeTheParent{ int a = 5;}unsafe
你 可使用 unsafe 修飾符在 C# 中定義一個不安全上下文。在不安全上下文中,你能夠插入不安全代碼,如 C++ 的指針等。參見如下代碼:複製內容到剪貼板代碼:public unsafe MyFunction( int * pInt, double* pDouble){ int* pAnotherInt = new int; *pAnotherInt = 10; pInt = pAnotherInt; ... *pDouble = 8.9; }接口
若是你有 COM 的思想,你立刻就知道我在說什麼了。接口是隻包含函數簽名而在子類中實現的抽象基類。在 C# 中,你能夠用 interface 關鍵字聲明這樣的接口類。.NET 就是基於這樣的接口的。C# 中你不能對類進行多重繼承——這在 C++ 中是容許的。經過接口,多重繼承的精髓得以實現。即你的子類能夠實現多重接口。(譯註:由此能夠實現多重繼承)複製內容到剪貼板代碼:using System;interface myDrawing{ int originx { get; set; } int originy { get; set; } void Draw(object shape); }
class Shape: myDrawing{ int OriX; int OriY;public int originx { get{ return OriX; } set{ OriX = value; } } public int originy { get{ return OriY; } set{ OriY = value; } } public void Draw(object shape) { ... // 作要作的事}// 類自身的方法 public void MoveShape(int newX, int newY) { ..... }}數組
數組在 C# 中比 C++ 中要高級不少。數組分配於堆中,因此是引用類型的。你不能訪問數組邊界外的元素。因此 C# 防止你引起那種 bug。同時也提供了迭代數組元素的幫助函數。foreach 是這樣的迭代語句之一。C++ 和 C# 數組的語法差別在於:
方括號在類型後面而不是在變量名後面建立元素使用 new 運算符C# 支持一維、多維和交錯數組(數組的數組)
例子:複製內容到剪貼板代碼:int[] array = new int[10]; // int 型一維數組for (int i = 0; iarray.Length; i++)array = i;
int[,] array2 = new int[5,10]; // int 型二維數組array2[1,2] = 5;
int[,,] array3 = new int[5,10,5]; // int 型三維數組array3[0,2,4] = 9;
int[][] arrayOfarray = new int; // int 型交錯數組 - 數組的數組arrayOfarray[0] = new int; arrayOfarray[0] = new int[] {1,2,15};
[8][9][10] ...
索引器
索引器用於書寫一個能夠經過使用 [] 像數組同樣直接訪問集合元素的方法。你所須要的只是指定待訪問實例或元素的索引。索引器的語法和類屬性語法相同,除了接受做爲元素索引的輸入參數外。
例子:
注 意:CollectionBase 是用於創建集合的庫類。List 是 CollectionBase 中用於存放集合列表的受保護成員。複製內容到剪貼板代碼:class Shapes: CollectionBase {public void add;...void SetDay(int day) {....}按引用傳遞/輸入-輸出參數
C++ 中的引用參數是經過指針或引用運算符 & 傳遞的。C# 中的引用參數更不易出錯。你能夠傳遞一個引用地址,你傳遞一個輸入的值並經過函數獲得一個輸出的值。所以引用參數也被稱爲輸入-輸出參數。
你不能將未初始化的引用參數傳遞給函數。C# 使用關鍵字 ref 指定引用參數。你同時還必須在傳遞參數給要求引用參數的函數時使用關鍵字 ref。
例 子:複製內容到剪貼板代碼:int a= 5;FunctionA(ref a); // 使用 ref,不然將引起編譯時錯誤Console.WriteLine(a); // 打印 20複製內容到剪貼板代碼:void FunctionA(ref int Val){ int x= Val;Val = x* 4; }輸出參數
輸出參數是隻從函數返回值的參數。輸入值不要求。C# 使用關鍵字 out 表示輸出參數。
例子:複製內容到剪貼板代碼:int Val; GetNodeValue(Val);複製內容到剪貼板代碼:bool GetNodeValue(out int Val) { Val = value; return true;}參數和數組的數量變化
[8][9][10] ...
C# 中的數組使用關鍵字 params 進行傳遞。一個數組類型的參數必須老是函數最右邊的參數。只有一個參數能夠是數組類型。你能夠傳送任意數量的元素做爲數組類型的參數。看了下面的例子你能夠更好的理解:
注意:使用數組是 C# 提供用於可選或可變數量參數的惟一途徑。
例 子:複製內容到剪貼板代碼:void Func(params int[] array) { Console.WriteLine("number of elements {0}", array.Length); }複製內容到剪貼板代碼:Func(); // 打印 0 Func(5); // 打印 1 Func(7,9); // 打印 2 Func(new int[] {3,8,10}); // 打印 3 int[] array = new int[8] {1,3,4,5,5,6,7,5}; Func(array); // 打印 8運算符與表達式
運算符和表達式跟 C++ 中徹底一致。然而同時也添加了一些新的有用的運算符。有些在這裏進行了討論。
is 運算符
is 運算符是用於檢查操做數類型是否相等或能夠轉換。is 運算符特別適合用於多態的情形。is 運算符使用兩個操做數,其結果是布爾值。參考例子:複製內容到剪貼板代碼:void function(object param){if(param is ClassA) //作要作的事 else if(param is MyStruct) //作要作的事}}as 運算符
as 運算符檢查操做數的類型是否可轉換或是相等(as 是由 is 運算符完成的),若是是,則處理結果是已轉換或已裝箱的對象(若是操做數能夠裝箱爲目標類型,參考 裝箱/拆箱)。若是對象不是可轉換的或可裝箱的,返回值爲 null。看看下面的例子以更好的理解這個概念。複製內容到剪貼板代碼:Shape shp = new Shape(); Vehicle veh = shp as Vehicle; // 返回 null,類型不可轉換
Circle cir = new Circle(); Shape shp = cir; Circle cir2 = shp as Circle; //將進行轉換
object[] objects = new object;objects[0] = "Aisha";object = new Shape();
string str;for(int i=0; i& objects.Length; i++){ str = objects as string; if(str == null) Console.WriteLine("can not be converted"); else Console.WriteLine("{0}",str);}複製內容到剪貼板代碼:Output:Aishacan not be converted語句
除了些許附加的新語句和修改外,C# 的語句和 C++ 的基本一致。
如下是新的語句:
foreach
用於迭代數組等集合。
例子:複製內容到剪貼板代碼:foreach (string s in array)Console.WriteLine(s);lock
在 線程中使代碼塊稱爲重點部分。(譯註:lock 關鍵字將語句塊標記爲臨界區,方法是獲取給定對象的互斥鎖,執行語句,而後釋放該鎖。lock 確保當一個線程位於代碼的臨界區時,另外一個線程不進入臨界區。若是其餘線程試圖進入鎖定的代碼,則它將一直等待(即被阻止),直到該對象被釋放。)
checked/unchecked
用於數字操做中的溢出檢查。
例子:複製內容到剪貼板
[8][9][10] ...
代碼:int x = Int32.MaxValue; x++; // 溢出檢查 { x++; // 異常}unchecked{x++; // 溢出}下面的語句已修改:(譯註:原文如此,疑爲做者筆誤)Switch
Switch 語句在 C# 中修改過。
1.如今在執行一條 case 語句後,程序流不能跳至下一 case 語句。以前在 C++ 中這是能夠的。
例 子:複製內容到剪貼板代碼:int var = 100;switch (var) {case 100: Console.WriteLine("Value is 100"); // 這裏沒有 breakcase 200: Console.WriteLine("Value is 200"); break; }C++ 的輸出:複製內容到剪貼板代碼:Value is 100Value is 200而在 C# 中你將獲得一個編譯時錯誤:複製內容到剪貼板代碼:error CS0163: Control cannot fall throughfrom one case label (‘case 100:‘) to another2.然而你能夠像在 C++ 中同樣這麼用:複製內容到剪貼板代碼:switch (var) { case 100:case 200: Console.WriteLine("100 or 200VALUE is 200"); break; }3.你還能夠用常數變量做爲 case 值:
例子:複製內容到剪貼板代碼:const string WeekEnd = "Sunday";const string WeekDay1 = "Monday";
....
string WeekDay = Console.ReadLine();switch (WeekDay ) { case WeekEnd: Console.WriteLine("It‘s weekend!!"); break; case WeekDay1: Console.WriteLine("It‘s Monday"); break;
}
委託
委託讓咱們能夠把函數引用保存在變量中。這就像在 C++ 中使用 typedef 保存函數指針同樣。
委託使用關鍵字 delegate 聲明。看看這個例子,你就能理解什麼是委託:
例 子:複製內容到剪貼板代碼:delegate int Operation(int val1, int val2);public int Add(int val1, int val2) {return val1 + val2; }public int Subtract (int val1, int val2) {return val1- val2;}
public void Perform(){ Operation Oper; Console.WriteLine("Enter + or - "); string optor = Console.ReadLine(); Console.WriteLine("Enter 2 operands");string opnd1 = Console.ReadLine(); string opnd2 = Console.ReadLine();int val1 = Convert.ToInt32 (opnd1);int val2 = Convert.ToInt32 (opnd2);if (optor == "+") Oper = new Operation(Add); else Oper = new Operation(Subtract);Console.WriteLine(" Result = {0}", Oper(val1, val2));}
繼承與多態
[8][9][10] ...
C# 只容許單一繼承。多重繼承能夠經過接口達到。
例子:複製內容到剪貼板代碼:class Parent{}
class Child : Parent虛函數
虛 函數在 C# 中一樣是用於實現多態的概念的,除了你要使用 override 關鍵字在子類中實現虛函數外。父類使用一樣的 virtual 關鍵字。每一個重寫虛函數的類都使用 override 關鍵字。(譯註:做者所說的「一樣」,「除……外」都是針對 C# 和 C++ 而言的)複製內容到剪貼板代碼:class Shape{ public virtual void Draw() { Console.WriteLine("Shape.Draw") ; }}
class Rectangle : Shape
{ public override void Draw() { Console.WriteLine("Rectangle.Draw"); } }
class Square : Rectangle{ public override void Draw() { Console.WriteLine("Square.Draw"); }}class MainClass{ static void Main(string[] args) { Shape[] shp = new Shape; Rectangle rect = new Rectangle();shp[0] = new Shape(); shp = rect; shp = new Square();shp[0].Draw(); shp.Draw(); shp.Draw(); }}Output:Shape.DrawRectangle.DrawSquare.Draw
使用「new」隱藏父類函數
你能夠隱藏基類中 的函數而在子類中定義其新版本。關鍵字 new 用於聲明新的版本。思考下面的例子,該例是上一例子的修改版本。注意輸出,我用 關鍵字 new 替換了 Rectangle 類中的關鍵字 override。複製內容到剪貼板代碼:class Shape{ public virtual void Draw() { Console.WriteLine("Shape.Draw") ; }}
class Rectangle : Shape{ public new void Draw() { Console.WriteLine("Rectangle.Draw"); } }class Square : Rectangle{ //這裏不用 override public new void Draw(){ Console.WriteLine("Square.Draw"); }}class MainClass{ static void Main(string[] args) { Console.WriteLine("Using Polymorphism:"); Shape[] shp = new Shape; Rectangle rect = new Rectangle();shp[0] = new Shape(); shp = rect; shp = new Square();shp[0].Draw(); shp.Draw(); shp.Draw();Console.WriteLine("Using without Polymorphism:"); rect.Draw();Square sqr = new Square(); sqr.Draw(); }}Output:Using PolymorphismShape.DrawShape.DrawShape.DrawUsing without Polymorphism:Rectangle.DrawSquare.Draw
多態性認爲 Rectangle 類的 Draw 方法是和 Shape 類的 Draw 方法不一樣的另外一個方法,而不是認爲是其多態實現。因此爲了防止父類和子類間的命名衝突,咱們只有使用 new 修飾符。
[8][9][10] ...
注 意:你不能在一個類中使用一個方法的兩個版本,一個用 new 修飾符,另外一個用 override 或 virtual。就像在上面的例子中,我不能在 Rectangle 類中增長另外一個名爲 Draw 的方法,由於它是一個 virtual 或 override 的方法。一樣在 Square 類中,我也不能重寫 Shape 類的虛方法 Draw。
調用基類成員
若是子類的數據成員和基類中的有一樣的名字,爲了不 命名衝突,基類成員和函數使用 base 關鍵字進行訪問。看看下面的例子,基類構造函數是如何調用的,而數據成員又是如何使用的。複製內容到剪貼板代碼:public Child(int val) :base(val){ myVar = 5; base.myVar;}
OR
public Child(int val){ base(val); myVar = 5 ; base.myVar;}
前景展望
本 文僅僅是做爲 C# 語言的一個快速瀏覽,以便你能夠熟悉該語言的一些特性。儘管我嘗試用實例以一種簡短而全面的方式討論了 C# 幾乎全部的主要概念,但我認爲仍是有不少內容須要增長和討論的。之後,我會增長更多的沒有討論過的命令和概念,包括事件等。我還想給初學者寫一下怎麼用 C# 進行 Windows 編程。