最簡單的C#快速入門教程

在一小時內學會 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/職業:團隊帶頭人位置:英國

簡介

C# 是一種具有 C++ 特性,Java 樣式及 BASIC 快速建模特性的編程語言。如果你已經知曉 C++ 語言,本文將在不到一小時的時間內帶你快速瀏覽 C# 的語法。如果熟悉 Java 語言,Java 的編程結構、打包和垃圾回收的概念肯定對你快速學習 C# 大有幫助。所以我在討論 C# 語言構造的時候會假設你知道 C++。

本文通過一系列例程以簡短但全面的方式討論了 C# 語言構造和特性,所以你僅需略覽代碼片刻,即可瞭解其概念。

注意:本文不是爲 C# 宗師而寫。有很多初學者的 C# 文章,這只是其中之一。

接下來關於 C# 的討論主題:

編程結構 命名空間 數據類型 變量 運算符與表達式 枚舉 語句 類與結構 修飾符 屬性 接口 函數參數 數組 索引器 裝箱與拆箱 委託 繼承與多態以下主題不會進行討論:

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 編程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章