1 緒論
c# 是一種簡練,時髦(?),面向對象(object oriented),類型可靠(type-safe)的
編程語言。它(發音:C sharp)是從c/c++發展而來的(?俺覺得更象是java),和c/c++
是一個語系。所以,很容易被c/c++的程序員接受。c#的目標是結合Visual Basic的高產和
C++質樸的力量。
c#將會是vs7的一分子。vs7還支持vb,vc和標記語言——VBScript和JScript。所有這些語言
都會在Next Generation Windows Services (NWGS) platform 中得到支持(c#就需要一個
NWGS SDK包,可以在m$的網站上下載)。有了這個東東(NWGS),c#就不需要自己的類庫,
而使用vc或vb這樣一些成熟的庫。c#也確實沒有自己的類庫。
廢話完了。
1。1 一個老土的例子(就不能換換嗎?)*/
/* idontlikeHelloworld.cs : such a out sample :( */
1: using System;
2: class idontlikeHelloworld
3: {
4: static void Main() {
5: Console.WriteLine("i dont like Hello world");
6: Console.ReadLine();
7: }
8: }
/* 如果俺要出書的話,會考慮換個好點的例子。 ^&^
先說說怎樣運行。首先,你需要windows2000!(是的,就是它,請各位不要隨地丟果皮——
整個香蕉丟給俺就可以了。)然后,需要NWGS SDK!(82.4mb,不算很大噢。嘿嘿,好在
它沒有自己的類庫。)安裝后,在你的程序所在的目錄下鍵入:
csc idontlikeHelloworld.cs (加上一個回車鍵)
是不是有點復古的味道?這個操作會在和你的*.cs相同目錄下產生一個
idontlikeHelloworld.exe文件。雙擊它,距可以看見:
i dont like Hello world
回車就可以結束它,非常簡單。不過,也可以這樣:把它存成后綴為.c的文件更好
(即:idontlikeHelloworld.c)。這樣就可以用vc的IDE進行打字,編輯。vc的
txt editor是最棒的噢(又要vc,NO!!!)。然后:
csc idontlikeHelloworld.c (加上一個回車鍵)
最終效果是完全一樣的。好,現在分析語法:(c#在語法上完全沒有新意 :-| )
1: using System;
using 其實是c++的關鍵字,在c#中的含義也相仿(就是說俺還不敢100%肯定,抱歉)。using
用在另一個關鍵字namespace之后。還是先看看namespace。
語法(syntax):(from MSDN)
namespace [identifier] { namespace-body }
俺的理解:
identifier:在這里就是System(請記住:c#和c/c++一樣,是區分大小寫的!)。System
必須在使用它的范圍內是唯一的。即,不能夠有第二個System,但可以有system。
而“它的范圍”,俺不想詳細解說,只有在實踐中才可能掌握。而且,初學者根本
不必知道!俺也是近來才知道還有個namespace和using。 :)
在{ namespace-body }中的是真正有用的東東,包括第五行的“Console.WriteLine”的聲明和
定義(后面還會提到)。System是由NWGS定義的,咱們只需用(using)它即可。至于System在
什么文件里定義,咱就不用管了!交給編譯器(就是剛才那個“csc.exe”)去尋找。這就代替
了c/c++中的“#i nclude”,可以說是近了一步,避免大量煩人的細節。如果你沒學過c/c++,
就不用理會。namespace 在后面還會談到。
2: class idontlikeHelloworld
class:是c語系中另一個關鍵字“類”。表示一系列的特性(官方說法:屬性)和行為方法,有
了它你的程序就可以“另類”,創造與別不同的有你特色的東東噢!在這里,俺就定義了
“idontlikeHelloworld”。注意:這也是c#強制的,對于每一個可執行的程序都必須有。你想干
的事就可以記錄在緊跟著你定義的class后面的一對花括號。注意:“{”和“}”一一對應的,
“(”和“)”同樣。
4: static void Main() {
Main()是本例子第一個動作(行為方法),干的第一件事。它是屬于俺定義的idontlikeHelloworld
類的方法。并且是c#強制的,是程序的真正開始!在緊跟在它后面的“{}”中的語句順序,就是程序
的運行順序!本例中只有一行(第六行干嘛用?你可以去掉再編譯一次看看),輸出一句話。
5: Console.WriteLine("i dont like Hello world");
非常奇怪,Console(再次提醒:注意大小寫)不是俺定義的,從何而來?它其實是屬于System
namespace 的一個class。WriteLine()是Console類中的一個方法,用來顯示一句話(字符串)。
這里只是用了這個方法的1/18!并且是最簡單之一!其他的有機會再說。你也可以用
“Console.WriteLine”在“NGWS SDK Documentaion”中搜索“Console.WriteLine”,記住復選
“僅搜索標題”,它會列出19項。好啦,完了!其實,還有“.”沒說呢!呵呵...lei si la!!!!
(續前)
“.”被稱為分隔符(separator),用來連接名字,如上面的“Console.WriteLine”,就把類和它的
方法連接。通過這種方式,咱們就可以使用現成方法集合。這里再回顧一下俺的例子,看看namespace和
“.”是如何連用的,還有為什么要使用namespace這個關鍵字。把例子稍微改一下:*/
/* idontlikeHelloworld.cs */
1: //using System;
2: class idontlikeHelloworld
3: {
4: static void Main() {
5: System.Console.WriteLine("i dont like Hello world");
6: System.Console.ReadLine();
7: }
8: }
/* 看見了,當俺注銷掉“using System;”后,在第五行和第六行加了“System”。程序的結果不會改
變。但是,很明顯的這樣比較羅嗦,所以引入了“namespace”。其實,class應該可以完成同樣的功能。
不過,設計者可能不想讓一個關鍵字涵蓋太多的功能。記得在c向c++發展的時候,引入了“class”,而
不是擴展“struct”關鍵字的功能;又比如“=”只用于賦值,“==”只用于判斷相等。這是c/c++和c#
在語法上其中一個重要的特點。這樣設計的好處很多。有機會再聊噢。
如果你沒學過c/c++,以下的內容可以跳過。c#與c/c++在語法上還是有區別的,比如:
1。c#根本沒有“::”;“->”只在程序中很小的片斷中。在c#中應采用“.”。
2。c#無須先聲明定義,再使用。與java相同。
3。c#取消了用“#i nclude”導入其他的程序文本文件,而采用象征性的句柄引入他人的代碼。這樣一來,
就排除了編程語言間的障礙,方便地使用其它語言編寫的庫。如“Console”類可以是c#或者是其他任一種語言編寫的。
1。2 自動化的內存管理(Automatic memory management)
手動管理內存需要程序員自行分配和釋放內存塊。這要求程序員有清晰的頭腦和對整個運行過程有十分的
把握(好難!)。而c#把程序員從這難以承擔的任務中解放出來。在多數的情況下,這種自動內存管理提
高代碼的質量和程序員的生產力。并且,不會對程序的意圖和執行產生幅面的影響(?俺可不相信m$的鬼
話)。不過,估計比java的回收站好一點吧。因為c#出道遲嘛(盡胡扯)。好了,來看看例子。*/
using System;
public class Stack
{
private Node first = null;
public bool Empty {
get {
return (first == null);
}
}
public object Pop() {
if (first == null)
throw new Exception("Can't Pop from an empty Stack.");
else {
object temp = first.Value;
first = first.Next;
return temp;
}
}
public void Push(object o) {
first = new Node(o, first);
}
class Node
{
public Node Next;
public object Value;
public Node(object value): this(value, null) {}
public Node(object value, Node next) {
Next = next;
Value = value;
}
}
}
class Test
{
static void Main() {
Stack s = new Stack();
for (int i = 0; i < 10; i++)
s.Push(i);
while (!s.Empty)
Console.WriteLine(s.Pop());
}
}
/*
stack類實現了一系列Node的實例。大家可以看看stack類的Push方法。Node的實例就是在Push方法中創建的。
就是“first = new Node(o, first);”。請記住這個“new”噢。它就是用來創建類實例的。相關的語法太
多,遛到后面用一節詳細講。這里只是要了解自動內存管理(Automatic memory management)好處?!“new”
是負責初始化類實例。而在c/c++中釋放這些實例要用另一個關鍵字“delete”。但是在什么時候用delete呢,
這通常是很費神的活,老手也會陰溝里翻船。何況是俺呢!但在c#中有不用了。例子里就沒有用“delete”。
當Node的實例不需要時,垃圾收集器(garbage collector)自動銷毀它,不用俺操心嘍。這點到和java挺
像的(可能是抄的)。
在一個test類里,俺用了一個循環,對stack類的實例的Push方法賦值十次。于是,Push創建了Node的十個實
例(instance)。然后用Pop把它們顯示出來。其順序正好與創建的順序相反。
這個例子相當的好,是stack
的一個典型,也很好的表述了自動內存管理的機制。但也不好懂,好在這一節不是寫給毫無基礎的網友看的。
俺自個都花了幾分鐘看明白,各位大蝦更是沒問題。
其實,當顯示完了“10”以后,就會有一個Node的實例符合被釋放的條件,但垃圾收集器并不一定會這樣做。
也就是說,它的行為并不確定(這和java一樣,俺猜)。有時候,這種行為會帶來一些負面影響。起碼是性
能降低。自動內存管理本身也是有問題的。因為它很難管理一些特殊情況。有一些關于java的垃圾收集器的
文章也有提到。m$也不會好得了多少。所以,m$有個不安全代碼的術語(unsafe code),用來為高級用戶服
務。即,用戶可以不采用垃圾收集器。但必須用“unsafe”關鍵字顯式聲明之。這樣就避免了用戶不經意以
外使用不安全代碼。下面是一個例子:*/
using System;
class Test
{
unsafe static void WriteLocations(byte[] arr) {
fixed (byte *p_arr = arr) {
byte *p_elem = p_arr;
for (int i = 0; i < arr.Length; i++) {
byte value = *p_elem;
string addr = int.Format((int) p_elem, "X");
Console.WriteLine("arr[{0}] at 0x{1} is {2}", i, addr, value);
p_elem++;
}
}
}
static void Main() {
byte[] arr = new byte[] {1, 2, 3, 4, 5};
WriteLocations(arr);
}
}
/*
俺對這個例子不是很滿意,也讓俺有點迷惑,有機會再自己寫一個。很簡單,只是可以用指針了!萬歲!
其實,俺對這一節最沒有把握了!有不少地方都不能自圓其說!所以,請各位大蝦大力批評。*/
1。3 類型
c#支持兩種基本的類型:一種是值(value types),一種是引用(reference types)。值包括簡單類型
(char、int、和float),枚舉(enum)和結構(struct)。引用包括類(class),界面(interface),
代表(delegate)和數組陣列(array)。值與引用不同之處在于:值直接存儲它的數據內容;而引用存儲對象
的引用。是不是粉費解?!打個比方吧。你在某地買了套別墅(好棒噢)。卻從未去過,只知道地址,怎
么辦?你可以坐出租車,司機看了地址就知道怎樣走不用你操心。你手里的地址就好像對象的名字,你把
它寫在程序中,就好像把地址給了司機。司機就是你的編譯器,它知道該去哪。你豪華的房子就好比那個
NGWS SDK開發包(82mb噢,夠豪華了!俺的m啊--就這樣燒嘍)。房子里有你想要的東東,比如你想寫一句
話(i dont like Hello world),就好像上面例子,要用到“WriteLine”。于是,你就給出“WriteLine”
的地址,比如:“Console.WriteLine”。明白?!俺可累了。zzz... (強打精神)不知道你想到沒有,
值和引用的區別可以引出一個重要特性。值的變量和變量存儲的數據是一一對應的,唯一性。而引用則不
然。引用中不同的變量可以引用同一個對象的實例。當其中一個變量改變實例的值時,其他引用這個實例的
變量也會受到影響(當然,變量本身并沒有改變,即,地址沒變)。瞧,變量只是說明存儲對象的位置(地
址),而不是對象本身。就好像你漂亮的房子被燒了,但你的地址并沒有改變,但地址對應的房子就沒了。
也許是別人也有這個地址,他去燒了你的房子!好了,在給個例子:*/
1: using System;
2: class CValue
3: {
4: public int Value = 0;
5: }
6: class Test
7: {
8: static void Main() {
9: int val1 = 0;
10: int val2 = val1;
11: val2 = 123;
12: CValue ref1 = new CValue();
13: CValue ref2 = ref1;
14: ref2.Value = 123;
15: Console.WriteLine("Values: {0}, {1}", val1, val2);
16: Console.WriteLine("Refs: {0}, {1}", ref1.Value, ref2.Value);
17: }
18: }
/* 下面是輸出的結果:
Values: 0, 123
Refs: 123, 123
啊哈,應該粉清楚了吧。變量val1和變量val2互不影響,它們各自有自己的存儲空間。而ref2復制
了ref1,所以,它們引用了同一個對象的實例。當改變它們其中一個的時候,就會影響到另一個的
值。
1。5 數組類型(Array types)
數組可以是一維的,也可是多維的。數祖的成員可以是整齊的,也可以是變長(jagged)的。
一維的數組是最普通,最簡單的。這里值給出一個例子,就不多解釋了。*/
using System;
class Test
{
static void Main() {
int[] arr = new int[5];
for (int i = 0; i < arr.Length; i++)
arr[i] = i * i;
for (int i = 0; i < arr.Length; i++)
Console.WriteLine("arr[{0}] = {1}", i, arr[i]);
}
}
/* 結果如下:
arr[0] = 0
arr[1] = 1
arr[2] = 4
arr[3] = 9
arr[4] = 16
我們還可以比較的看看多維,規則,變長的數組的定義和賦值:*/
class Test
{
static void Main() {
int[] a1 = new int[] {1, 2, 3}; //一維
int[,] a2 = new int[,] {{1, 2, 3}, {4, 5, 6}}; //二維
int[,,] a3 = new int[10, 20, 30]; //三維
int[][] j2 = new int[3][]; //變長
j2[0] = new int[] {1, 2, 3};
j2[1] = new int[] {1, 2, 3, 4, 5, 6};
j2[2] = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
}
}
/*
上面的例子給出了各種樣式的數組。變量a1、a2和a3是規則數組。j2則是變長的數組。
規則數組很容易就可以計算出它們的長度。比如a3的長度是:10*20*30=6000。相反,變長
數組就有點不同,它的每一個維度都必須單獨定義。如j2的第一維度是3,第二個是6,第
三個是9,所以總長度是:1*3+1*6+1*9=18。
上面對數組的賦值是嚴謹的風格,在某種情況下,我們可以簡化寫法,但我總覺得這種簡化
應用限制太多,容易出錯。在這里就不作介紹了。這里再給一個例子說明函數中的參數如何
賦值*/
class Test
{
static void F(long[] arr) {}
static void Main() {
F(new longt[] {1, 2, 3});
}
}
趕出一編!請指正!
1。6 統一系統類型(Type system unification)
c#獨創了一種類型——統一系統類型(為了這個累刑,我頭疼死了。誰有更好的名字,請務必告訴
我)。總之,所有的其他類型,包括值和引用,都可以被當作統一系統類型來對待。從概念上說,
所有的類型都從它派生。這樣,其他的類型就可以使用統一系統類型的屬性和方法。包括一些“簡
單”類型,如:int。還是給個例子吧:*/
using System;
class Test
{
static void Main() {
Console.WriteLine(3.ToString());
}
}
/*“3.ToString()”調用了object的“ToString()”方法。相信學過c/c++的朋友都知道要輸出一個
數字有多麻煩,現在就省事了。再看一個:*/
class Test
{
static void Main() {
int i = 123;
object o = i; // boxing
int j = (int) o; // unboxing
}
}
/* 這個像帽子戲法的例子中,從“int”轉換成“object”,又轉換回來。這樣一來,在值和引用
之間就架起了一座橋梁。這樣有什么用呢。即興舉一個常見的例子...就min把。在c/c++中:*/
// c/c++ code
void min(int i, int j)
{
return ((i < j) ? i : j);
}
/* 如果比較的不是int,或者說可能是int,也可能是float、double呢?可以這樣:*/
template<class T>
T min (T i, T j)
{
return ((i < j) ? i : j)
}
/* 用c#可以:*/
void swap (object a, object b)
{
return ((i < j) ? i : j);
}
/* 我想大家一定看出來第二個例子要比較一個int和一個float的話,還需要一些轉換,而第三個
例子就可以比較所有的變量!這個靈活度簡直太大了。所以,我私以為,大家使用時一定要小心!
它在比較一個int和一個class的時候決不會報錯的。呵呵,我發現我的翻譯總是越跑越遠,總是
扣不住原文。篡改甚多,敬請原諒!
1。7 語句(Statements)
c#借用了c/c++大多數的語句方法,不過仍然有些值得注意的地方。還有些地方是有所改動的。
在這里,我只提一些c#特有的東東。
1。7。10 “foreach”語句
“foreach”語句列舉一個集合內的所有元素,并對這些元素執行一系列的操作。還是看看例子吧:*/
using System;
using System.Collections;
class Test
{
static void WriteList(ArrayList list) {
foreach (object o in list)
{
int i = (int) o;//如果是for語句,這里一定會報錯!
Console.WriteLine(0);
Console.WriteLine(++i);
}
}
static void Main() {
ArrayList list = new ArrayList();
for (int i = 0; i < 10; i++)
list.Add(i);
WriteList(list);
}
}
/*這個例子用“foreach”掃描了整個“list”,并把“list”中所有的元素打印出來。有時候還是
挺方便的。
1。7。15 安全檢查開關(The checked and unchecked statements)
“checked”和“unchecked”語句用來控制數學運算和完整類型轉換的檢查工作。“checked”檢查它
作用的域中可能出現的違例,并拋出一個異常;而“unchecked”則阻止所有的檢查。舉個例子:*/
using System;
class Test
{
static int x = 1000000;
static int y = 1000000;
static int F() {
checked {return (x * y);} // 拋出 OverflowException
}
static int G() {
unchecked {return (x * y);} // 返回 -727379968
}
static int H() {
return x * y; // 缺省狀態。
}
static void Main() {
F(); //可以注銷掉此行試試。
Console.WriteLine(G());
Console.WriteLine(H());
}
}
/*
在編譯過程中不會有任何錯誤出現。因為“checked”和“unchecked”只在運行時才起作用。值得一說的是
H()。它的缺省狀態和編譯器當前的缺省溢出檢查的狀態有關。但返回的結果肯定和F()或G()中的任一個相同。
再看一個例子:*/
using System;
class Test
{
const int x = 1000000;
const int y = 1000000;
static int F() {
checked {return (x * y);} // 編譯器警告(Compile warning):溢出(overflow)
}
static int G() {
unchecked {return (x * y);} // 返回 -727379968
}
static int H() {
return x * y; // 編譯器警告(Compile warning):溢出(overflow)
}
static void Main() {
Console.WriteLine(F()); //可以注銷掉此行試試。
Console.WriteLine(G());
Console.WriteLine(H()); //可以注銷掉此行試試。
}
}
/* 當F()和H()求值的時候,就會引起一個編譯警告。而在G()中,因為有了“unchecked”,屏蔽了這個警
告。要注意的是“checked”和“unchecked”都不能對函數的返回值進行操作!比如:*/
class Test
{
static int Multiply(int x, int y) {
return x * y;
}
static int F() {
checked{ return Multiply(1000000, 1000000); } // 與 return Multiply(1000000, 1000000);
} // 有相同的效果。
}
/* 其實大家稍微想一下知道為什么m$沒有這么做!對這個內容的討論超出本文的范圍和俺的能力之外哦。
在c#中,所有的十六進制數都是uint。如果用強制類型轉換會引起編譯器報錯。用“unchecked”則可以
跳過這個機制,把uint的十六進制數轉化為int。如:*/
class Test
{
public const int AllBits = unchecked((int)0xFFFFFFFF);
public const int HighBit = unchecked((int)0x80000000);
}
/* 上例所有的常數都是uint,而且超過了int的范圍,沒有“unchecked”,這種轉換會引發一個編譯器錯
誤。注意:上面用的是“unchecked”操作符。不是語句。不過它們之間除了一個用“()”,另一個用
“{}”以外,幾乎一樣。BTW,“checked”同樣。
1。7。16 “lock”語句(The lock statement)
“lock”獲得一個相互排斥的對象鎖定。(俺查過一些資料,但都沒有清晰說明,暫不介紹)
文章來源:
http://www.tkk7.com/kuxiaoku/articles/94806.html
posted on 2007-01-19 00:14
苦笑枯 閱讀(315)
評論(0) 編輯 收藏 所屬分類:
C#