using System; namespace ConApp //命名空間 //C++中的#include語句包括了在編譯時的資源代碼,而C#中的using語句的作用僅僅是告訴編譯器在哪個命名空間中 //找到沒有被它們的完全限定名稱引用的類型,或者是不在默認狀態或全局命名空間中的類型。 //C#是完全面向對象的語言,意味着每樣東西(所有功能和數據項)都必須是類的一部分,除using語句外。 //在C++中,Main()是一個全局函數,但在C#中Main()是類的一部分。這裏的helloWorld之所以存在就是爲了 //保存Main()方法。Main()是helloWorld類的一個靜態成員,表示屬於類而不屬於任何一個實例 |
註釋
//或/**/用於註釋文本,而///是文檔註釋,用於從C#代碼中生成XML文檔
數據類型
*引用型
包括類、接口和數組
引用型數據的特性:
1.包括了類中的所有東西,例如,方法、屬性、構造函數等等
2.不管引用型是如何被實例化的,總是在託管堆上進行創建,如Foo f=new Foo(),這裏創建了一個Foo對象時,您所獲得的就是對Foo的一個引用。C#中的字符串是引用型的,但是並不都是使用new操作符來創建的
3.訪問引用型數據時需要使用引用!
4.傳遞拷貝、值和參數都要使用“引用”!
5.無用單元收集自動對引用數據類型進行處理(接口實際上不佔用內存,所以不被收集)
*值型
C#中內置的類型幾乎都是值型,是以位的正常次序存儲在程序堆棧上,具有更高效率。
創建值型數據不需要使用new關鍵字,而是使用標準的本地變量聲明的形式來完成。如
//Creating a value type
Point p;
可以使用struct或enum關鍵字創建新的數據類型。
如果結構使用了構造函數,可以使用new操作符來實例化一個結構,並以特定的值對其成員進行初始化。
值類型數據的特性:
1.值類型不能從引用類型中繼承,而且不能作爲超類來使用。
2.值型數據是在對堆棧中創建的,沒有涉及到分配和釋放單獨內存塊的操作,所以創建和破壞操作具有較高的效率。
3.可以被直接訪問,訪問效率很高。(因爲使用時不是引用)
4.默認情況下,值型數據用值來拷貝和傳遞的,並非用引用來完成。
5.不被無用單元回收器評價和收集。
裝箱與拆箱
爲什麼可以裝箱?因爲C#中所有的類型根本上都是從Object類中繼承而來,而Object類位於繼承樹上根部,因此定義的用來保存類型Object引用的通用容器,理所當然的可以保存任意的數據類型。
C#提供了一個根據需要將類型轉換到對象中以及從對象中轉換出來的方法,這意味着值類型具有了基本的位模式的高效率,而且還擁有了可以使用的對象。
當一個值型數據被裝箱時,系統其實是將其轉換爲一個對象:分配了一個對象實例,而且值型數據的值被拷貝到了箱中
拆箱操作有且只能使已被裝箱對象再轉換回一個值類型,該操作是使用強制類型轉換語法。如
//Box a value
int i=123; object o=i; |
//Unbox it
int j=(int)o; |
該強制類型轉換實際上表示“檢查裝箱的對象中的類型是否位int,如果是的話則提取該值並將它拷貝到j中”,否則將拋出一個InvalidCastException異常
C#內置的數據類型
除了string和object是引用類型外,其他的內置數據類型都是數值型的。有些並不符合通用語言規範中規定。
其中void類型表示一個方法的‘返回類型’,該類型表示什麼都不返回,而且不能將void作爲一個參數來使用。
字符型
C#包含了基本的char值類型和string引用類型,都使用16位unicode字符,是得有足夠大的空間來存放各種國家字符集。
常量
常量不能更改,使用const關鍵字聲明,但必須對其初始化。
const int m = 3; //必須初始化 const int n = m * 4; //能夠被編譯器計算出來的表達式 |
而以下的則是錯誤的
int p = 3; const int q=p+4; |
運算符(略)
創建和初始化變量
用戶能夠直接定義值型數據並對其進行初始化。如 float f=3.13F;
引用類型數據要用兩部分進行創建:引用變量和對象本身。如
//Creat a Person對象,假定Person爲引用型數據
Person p1 = new Person();
new運算符在託管堆上創建了一個新的Person對象,並返回了對這個對象的引用,而且將它賦給了p1。變量p1是一個引用變量,該變量指向了Person對象。
如以下代碼所示,只是將引用變量的值拷貝給另一個引用變量,而不是將前一個引用變量所指向的對象拷貝給另一個引用變量。
//Creat a new string and store the reference
string s1="abc"; string s2; s2=s1;// s2 and s1 refer to the same object |
變量比較
正如上段所述,在對引用數據進行比較時需要小心,因爲默認的情況下==和!=運算符比較的是引用的值,而不是對象的內容。(當然,我們可以重載運算符來比較引用的內容),如
using System; namespace ConApp if (n1 == n2) public Name(string s) |
這個控制檯應用程序用Ctrl+F5執行後的輸出結果爲This should not happen!。雖然我們創建了兩個包含同樣數值的實例,但是對兩個實例進行比較時失敗,因爲n1和n2這兩個引用指向了兩個不同的對象,它們不能“相等”。
如果想比較對象的內容,就需要使用Object類的四個通用方法Equals()方法。
在string類型中,爲了實現對字符串變量中內容的比較,需要重載==和!=運算符,如
using System; namespace ConApp if (s1 == s2) } |
Ctrl+F5後輸出結果爲Strings are equal!這裏重載了嗎?有點糊塗了
字符串構造
不使用new運算符而從字面值構造一個字符串。如,string str1="http://www.ebailu.com.cn";
但從“性能原因”考慮不應該使用從字面值構造一個字符串的構造函數?
數組
C#支持三種類型的數組:一維、矩形和參差。(第六章將有詳細介紹)
聲明一個一維數組,如
int[] array1=new int[10]; //這是一個值型數組
C#中的數組基於0的索引。
聲明並同時初始化一維數組:
int array1={1,2,3,4}; //常用方式 或 int[] array1=new int[]{1,2,3,4}; |
如果創建一個數組對象,但想在以後再對其進行初始化,則必須使用new語法,如下
//This is correct int[] array1; array1=new int[]{1,2,3,4};
//But this gives a compiler error int[] array1; array1={1,2,3,4}; |
選擇和循環控制語句
1.goto語句
該語句的功能是在switch語句中的情況之間進行跳轉,但在一個完全結構化的面嚮對象語言中不應使用該語句。
2.if語句
if語句中的表達式的計算機過必須是bool值。
if(expression1) {……} else if(expression2) {……} else …… |
3.for語句(循環)
for(initializer;condition;iterator) { statement; }
//如下面的語句是死循環endless loop for(;;) {……} |
4.中斷和繼續關鍵字
break和continue用於控制迭代循環
break語句可以立即從循環語句中退出
continue語句將終止當前的循環,並跳出到組成循環體的程序塊結尾
C#不支持帶有標記的中斷,因爲從嵌套循環中跳出來會使程序變得雜亂??
5.do……while語句和while語句(循環)
與while語句不通,do……while循環主體至少執行一次。如
6.foreach語句(循環)
該語句可以遍歷數組和集合中的元素,與VB中For Each結構相似。
該語句可以很好的與System.Collections命名空間所提供的幾個集合一起使用,如List和HashTable,抑或是實現Ienumerable的任意對象。(詳見第六章)
舉例:
int[] array1={1,2,3}; foreach (int i in array1) { Console.WriteLine("Value is {0}",i); } |
7.switch語句
switch表達式必須是常量,可能是整型(包括char型)或者string類型的。
如果要從一個Case“貫穿”到下一個Case,只是簡單的省略break語句是不夠的,必須使用“goto case 'Name'”。
定義和調用方法
[modifiers] return-type meth-name ([parameter-list]) //可以有N個修飾符(modifiers) //必須有返回類型,包括void類型(return-type) //可以不帶參數(parameter-list) |
參數如何傳遞給方法呢?
*如果方法參數是值型的,則傳遞給方法的是參數的拷貝,方法中對本地拷貝的修改不會影響到原始參數的值。
*如果參數是引用類型的,則所傳遞的是對象的一個引用,在方法中的任何修改都將影響到對象本身--沒有涉及到拷貝。
如何使用ref和out關鍵字?
*用ref關鍵字來標記一個參數就意味這對它的值所做的任何修改都會保存下來,就像一個引用型返回值一樣。如
void refMethod(ref int n) { n+=3; } …… int p=3; //ref參數必須初始化,否則編譯器報錯。 refMethod(ref p); //vallue of p will be changed //這裏的ref關鍵字告訴編譯器以引用型來傳遞返回值而不是以值型來傳遞。 |
* 用out關鍵字則使方法給參數賦一個值,而不是修改它的現有值。象ref一樣,out意味着方法中的參數引用指向了調用代碼中的原始變量。不同之處在於,ref參數必須在調用方法前被初始化,而out參數則不必初始化就可使用。
方法的重載
方法名相同,而參數數量或類型不同。
但是返回值類型不同的方法不是重載。
錯誤處理
在C#中無法拋出所有類型的實例,拋出值必須是System.Exception對象的一個引用(或是由一個派生類生成的對象)
*處理異常應該使用try,catch和finally程序塊。
注意:用戶必須正確排列catch程序塊,如果後一個catch程序塊沒有被執行,系統將拋出一個編譯錯誤。
而捕獲任何異常的catch程序塊稱爲常規捕獲子句,不應經常使用,因爲它沒有指定一個異常變量。如
try { //Code which may fail } catch { //handler error,遺失了異常帶來的信息,所以無法知道到底發生什麼錯誤。 } |
*finally程序塊
如果finally程序塊與try一起使用,那麼無論try程序塊是如何被執行或拋棄的,finally程序塊都將執行。
如果finally程序塊的過程給發生了異常將怎麼辦呢??
*異常類:均由System.Exception類派生
*選中與取消:關鍵字checked和unchecked可以用來爲算數運算和整型數據轉換控制溢出檢查。
着兩個關鍵字的特殊之處在於,既起着運算符的作用又起着語句的作用。
輸入/輸出(I/O)控制
System.Console類提供了控制檯的I/O
Console.In() //訪問標準的輸入流 Console.Out() //訪問標準的輸出流 Console.Error()//訪問標準的錯誤流 |
*輸入控制
Console.Read() //以int類型返回一個單獨字符,否則返回-1 Console.ReadLine() //回一個包含下一行輸入結果的字符串,否則返回null值。可以使用字符串類型的Length屬 |
*輸出控制
Console.Write() //輸出的值不帶換行符 Console.WriteLine() //出的值帶換行符 |