由C++轉(zhuǎn)向C#:需要注意哪些方面的變化?

2010-08-28 10:45:06來源:西部e網(wǎng)作者:

  手頭項(xiàng)目基本算完成了,沒有任務(wù),公司網(wǎng)站升級(jí)項(xiàng)目被放棄,學(xué)了一段時(shí)間java現(xiàn)被要求熟悉dotnet,還要熟悉基于ARM處理器的嵌入式linux平臺(tái),還有php。。。搞得這段老是忘記東西每隔10年左右,編程人員就需要花費(fèi)大量的時(shí)間和精力去學(xué)習(xí)新的編程技術(shù)。在80年代是Unix和C,90年代是Windows和C++,現(xiàn)在又輪到了微軟的.NETFramework和C#。盡管需要學(xué)習(xí)新的技術(shù),但由此帶來的好處卻遠(yuǎn)高于付出的勞動(dòng)。幸運(yùn)的是,使用C#和.NET進(jìn)行的大多數(shù)工程的分析和設(shè)計(jì)與在C++和Windows中沒有本質(zhì)的變化。在本篇文章中,我將介紹如何實(shí)現(xiàn)由C++到C#的飛躍。

  已經(jīng)有許多文章介紹過C#對(duì)C++的改進(jìn),在這里我就不再重復(fù)這些問題了。在這里,我將重點(diǎn)討論由C++轉(zhuǎn)向C#時(shí)最大的變化:由不可管理的環(huán)境向可管理的環(huán)境的變化。此外,我還會(huì)提出一些C#編程人員容易犯的錯(cuò)誤供大家參考,此外,還將說明一些C#語言的能夠影響編程的新功能。

  轉(zhuǎn)向可管理的環(huán)境

  C++的設(shè)計(jì)目標(biāo)是低級(jí)的、與平臺(tái)無關(guān)的面向?qū)ο缶幊陶Z言,C#則是一種高級(jí)的面向組件的編程語言。向可管理環(huán)境的轉(zhuǎn)變意味著你編程方式思考的重大轉(zhuǎn)變,C#不再處理細(xì)微的控制,而是讓架構(gòu)幫助你處理這些重要的問題。例如,在C++中,我們就可以使用new在棧中、堆中、甚至是內(nèi)存中的某一特定位置創(chuàng)建一個(gè)對(duì)象。

  在.NET的可管理環(huán)境中,我們?cè)俨挥眠M(jìn)行那樣細(xì)微的控制了。在選擇了要?jiǎng)?chuàng)建的類型后,它的位置就是固定的了。簡單類型(ints、double和long)的對(duì)象總是被創(chuàng)建在棧中(除非它們是被包含在其他的對(duì)象中),類總是被創(chuàng)建在堆中。我們無法控制對(duì)象是創(chuàng)建在堆中哪個(gè)位置的,也沒有辦法得到這個(gè)地址,不能將對(duì)象放置在內(nèi)存中的某一特定位置。(當(dāng)然也有突破這些限制的方法,但那是很另類的方法。)我們?cè)僖膊荒芸刂茖?duì)象的生存周期,C#沒有destructor。碎片收集程序會(huì)將對(duì)象所占用的內(nèi)存進(jìn)行回收,但這是非顯性地進(jìn)行的。

  正是C#的這種結(jié)構(gòu)反映了其基礎(chǔ)架構(gòu),其中沒有多重繼承和模板,因?yàn)樵谝粋(gè)可管理的碎片收集環(huán)境中,多重繼承是很難高效地實(shí)現(xiàn)的。

  C#中的簡單類型僅僅是對(duì)通用語言運(yùn)行庫(CLR)中類型的簡單映射,例如,C#中的int是對(duì)System.Int32的映射。C#中的數(shù)據(jù)類型不是由語言本身決定的,而是由CLR決定的。事實(shí)上,如果仍然想在C#中使用在VisualBasic中創(chuàng)建的對(duì)象,就必須使自己的編程習(xí)慣更符合CLR的規(guī)定。

  另一方面,可管理的環(huán)境和CLR也給我們帶來了好處。除了碎片收集和所有.NET語言中統(tǒng)一的數(shù)據(jù)類型外,它還提供給我們一個(gè)功能強(qiáng)大的面向組件的編程語言,無須對(duì)后期綁定提供特別的支持,類型發(fā)現(xiàn)和后期綁定都是被內(nèi)置在語言中的。屬性是C#語言中的第一類的成員,事件和代理也是。

  可管理環(huán)境最主要的優(yōu)點(diǎn)是.NETFramework。盡管在所有的.NET語文中都可以使用這種框架,但C#可以更好地使用.NET框架中豐富的類、接口和對(duì)象。

  Traps

  C#看起來與C++非常相似,這使得我們?cè)谟蒀++轉(zhuǎn)向C#時(shí)比較輕松,但其中也有一些容易出錯(cuò)的地方。在C++中編寫得非常漂亮的代碼,在C#中會(huì)不能通過編譯,甚至?xí)霈F(xiàn)意想不到的結(jié)果。C#與C++之間在語法上的變化并不大,編譯器能夠發(fā)現(xiàn)這二者之間大部分的差異,我在這里就不再多費(fèi)筆墨了,在這里我介紹幾個(gè)容易出問題的比較重要的變化:

  引用類型和值類型

  在C#中,值類型和引用類型數(shù)據(jù)是有區(qū)別的。簡單類型(int、long、double等)和結(jié)構(gòu)屬于值類型數(shù)據(jù),類和對(duì)象屬于引用類型數(shù)據(jù)。除非是包含在引用類型的變量中,與在C++中一樣,值類型變量的值存儲(chǔ)在棧中。引用類型的變量也存儲(chǔ)在棧中,但它的值是一個(gè)存儲(chǔ)在堆中的對(duì)象的地址,這一點(diǎn)也與C++類似。值類型變量是將自己的值傳遞給方法,而引用類型變量則將自己的指針傳遞給方法。

  結(jié)構(gòu)

  C#中的結(jié)構(gòu)與C++中有非常明顯的區(qū)別。在C++中,結(jié)構(gòu)更象是類,除了缺省的繼承外,其缺省的訪問權(quán)限是public而不是private。在C#中,結(jié)構(gòu)與類截然不同,它是用來封裝輕型對(duì)象的,是值類型的數(shù)據(jù)類型,在傳遞時(shí)傳送的是變量的值,而不是其地址。此外,它們也有一些不適用于類的限制,例如,它是不能繼承的,也沒有除System.ValueType之外的基本類。結(jié)構(gòu)還不能定義一個(gè)缺省的constructor。
  
  另一方面,由于結(jié)構(gòu)比類的效率要高,因此它非常適合于創(chuàng)建輕型對(duì)象。因此,如果它的缺點(diǎn)對(duì)你的軟件沒有影響,使用結(jié)構(gòu)比使用類效率要高得多,尤其是對(duì)于小對(duì)象而言。

  所有的一切都是對(duì)象

  在C#中,所有的東西都是由繼承Object得到的,包括創(chuàng)建的類和int、structs等值類型的變量。Object類提供了一些有用的方法,例如ToString,使用ToString的一個(gè)例子是與System.Console.WriteLine一起使用,它可以接受一個(gè)字符串和許多對(duì)象。與使用printf語句不同,要使用WriteLine,需要提供代換變量。假設(shè)myEmployee是用戶定義的Employee類的一個(gè)實(shí)例,myCounter是用戶定義的Counter類的一個(gè)實(shí)例:

Console.WriteLine("Theemployee:{0},thecountervalue:{1}",
myEmployee,myCounter);


  其中的WriteLine會(huì)調(diào)用每個(gè)對(duì)象的Object.ToString方法,替換作為參數(shù)返回的變量。如果Employee類不覆蓋ToString,就會(huì)調(diào)用缺省的實(shí)現(xiàn)(由System.Object繼承得到的),它將把類的名字作為一個(gè)字符串返回。Counter會(huì)覆蓋ToString,返回一個(gè)整型的變量,因此,上面代碼的輸出為:

Theemployee:Employee,thecountervalue:12


  如果向WriteLine傳遞一個(gè)整型變量會(huì)發(fā)生什么情況呢?由于不能對(duì)整型變量調(diào)用ToString,編譯器將自動(dòng)將整型變量封裝在一個(gè)對(duì)象的實(shí)例中。當(dāng)WriteLine調(diào)用ToString時(shí),對(duì)象就會(huì)返回表示整型變量值的字符串。下面的代碼就說明了這個(gè)問題:

  類的使用

usingSystem;
//不覆蓋ToString的類
publicclassEmployee
{
}
//覆蓋了ToString的類
publicclassCounter
{
privateinttheVal;
publicCounter(inttheVal)
{
this.theVal=theVal;
}
publicoverridestringToString()
{
Console.WriteLine("CallingCounter.ToString()");
returntheVal.ToString();
}
}
publicclassTester
{
publicstaticvoidMain()
{
//創(chuàng)建類的實(shí)例
Testert=newTester();
//調(diào)用非靜態(tài)成員
//(mustbethroughaninstance)
t.Run();
}
//演示調(diào)用ToString的非靜態(tài)方法
publicvoidRun()
{
EmployeemyEmployee=newEmployee();
CountermyCounter=newCounter(12);
Console.WriteLine("Theemployee:{0},thecountervalue:{1}",
myEmployee,myCounter);
intmyInt=5;
Console.WriteLine("Herearetwointegers:{0}and{1}",17,myInt);
}
}




  引用型參數(shù)和輸出型參數(shù)


  與C++中相同,C#中的方法也只能有一個(gè)返回值。在C++中,我們通過將指針或索引作為參數(shù)而克服了這個(gè)限制,被調(diào)用的方法改變其中的參數(shù),調(diào)用方法就可以得到新的值了。

  向方法中傳遞一個(gè)索引作為參數(shù)時(shí),只能嚴(yán)格地按傳遞索引或指針?biāo)軌蛱峁┑姆绞皆L問原來的對(duì)象。對(duì)于值類型變量而言,就不能采用這種方法了。如果要通過引用型參數(shù)傳遞值型變量,就需要在其前面加上ref關(guān)健字。如下所示:

publicvoidGetStats(refintage,refintID,refintyearsServed)


  需要注意的是,既需要在方法的定義中使用ref關(guān)健字,也需要在對(duì)方法的實(shí)際調(diào)用中使用ref關(guān)健字。

Fred.GetStats(refage,refID,refyearsServed);


  現(xiàn)在,我們可以在調(diào)用方法中定義age、ID和yearsServed變量,并將它們傳遞給GetStats,得到改變后的值。

  C#要求明確的賦值,也就是說,在調(diào)用GetStats方法之前,必須對(duì)age、ID和yearsServed這三個(gè)局部變量進(jìn)行初始化,這一工作似乎有點(diǎn)多余,因?yàn)槲覀儍H僅使用它們從GetStats中得到新的變量的值。為了解決這一問題,C#提供了out關(guān)健字,表示我們可以向方法中傳遞沒有被初始化的變量,這些變量將通過引用變量的方式進(jìn)行傳遞:

publicvoidGetStats(outintage,outintID,outintyearsServed)


  當(dāng)然了,調(diào)用方法也必須作出相應(yīng)的變化:

Fred.GetStats(outage,outID,outyearsServed);


  New的調(diào)用

  在C++中,new關(guān)健字可以在堆上生成一個(gè)對(duì)象。在C#中卻不是這樣。對(duì)于引用類型變量而言,new關(guān)健字在堆上生成一個(gè)對(duì)象;對(duì)于結(jié)構(gòu)等值類型變量而言,new關(guān)健字在棧中生成一個(gè)對(duì)象,并需要調(diào)用constructor。

  事實(shí)上,我們可以不使用new關(guān)健字而在棧上生成一個(gè)結(jié)構(gòu)類型的變量,但這時(shí)需要注意的是,New關(guān)健字能夠初始化對(duì)象。如果不使用new,則在使用前必須手工地對(duì)結(jié)構(gòu)中的所有成員進(jìn)行初始化,否則在編譯時(shí)會(huì)出錯(cuò)。

  對(duì)象的初始化

usingSystem;//有二個(gè)成員變量和一個(gè)構(gòu)造器的簡單結(jié)構(gòu)
publicstructPoint
{
publicPoint(intx,inty)
{
this.x=x;
this.y=y;
}

publicintx;
publicinty;
}

publicclassTester
{
publicstaticvoidMain()
{
Testert=newTester();
t.Run();
}

publicvoidRun()
{
Pointp1=newPoint(5,12);
SomeMethod(p1);//fine

Pointp2;//不調(diào)用new而直接創(chuàng)建

//編譯器編譯到這里時(shí)會(huì)出錯(cuò),因?yàn)閜2的成員變量沒有被初始化
//SomeMethod(p2);

//手工對(duì)它們進(jìn)行初始化
p2.x=1;
p2.y=2;

SomeMethod(p2);

}

//一個(gè)可以接受Point作為參數(shù)的方法
privatevoidSomeMethod(Pointp)
{
Console.WriteLine("Pointat{0}x{1}",
p.x,p.y);
}
}


  屬性

  大多數(shù)的C++編程人員都希望使成員變量的屬性為private,這種隱藏?cái)?shù)據(jù)的想法促進(jìn)了數(shù)據(jù)封裝概念的出現(xiàn),使我們能夠在不改變用戶依賴的接口的情況下而改變類的實(shí)現(xiàn)。通常情況下,我們只希望客戶獲取或設(shè)置這些成員變量的值。因此,C++編程人員開發(fā)出了用來存取private成員變量的存取器。

  在C#中,屬性是類的第一級(jí)成員。對(duì)于客戶而言,屬性看起來象一個(gè)成員變量。對(duì)于類的實(shí)現(xiàn)者而言,它看起來更象是方法。這種設(shè)計(jì)很巧妙,既可以實(shí)現(xiàn)數(shù)據(jù)的隱藏和封裝,又可以使客戶很方便地訪問成員變量。

  我們可以在Employee類中添加一個(gè)Age屬性,使客戶可以很方便地獲取和設(shè)置員工年齡這個(gè)類的成員:

publicintAge
{
get
{
returnage;
}
set
{
age=value;
}
}


  關(guān)健字value可以被屬性隱性地使用。如果編寫如下的代碼:

Fred.Age=17;


  編譯器將會(huì)把值17傳遞給value。

  通過只采用Get而不采用Set,我們可以為YearsServed創(chuàng)建一個(gè)只讀的屬性:

publicintYearsServed
{
get
{
returnyearsServed;
}
}Accessors的使用
privatevoidRun()
{
EmployeeFred=newEmployee(25,101,7);
Console.WriteLine("Fred'sage:{0}",
Fred.Age);
Fred.Age=55;
Console.WriteLine("Fred'sage:{0}",
Fred.Age);

Console.WriteLine("Fred'sservice:{0}",
Fred.YearsServed);
//Fred.YearsServed=12;//是不被允許的

}


  我們可以通過屬性獲取Fred的年齡,也可以使用這一屬性設(shè)置年齡。我們雖然可以訪問YearsServed屬性獲得它的值,但不能設(shè)置值。如果沒有注釋掉最后一行的代碼,在編譯時(shí)就會(huì)出錯(cuò)。

  如果以后決定從數(shù)據(jù)庫中獲取Employee的年齡,我們就只需要改變存取器的實(shí)現(xiàn),而客戶不會(huì)受到任何影響。

  數(shù)組

  C#提供了一個(gè)數(shù)組類,它比C/C++中傳統(tǒng)的數(shù)組更智能化。例如,在C#中寫數(shù)組時(shí)不會(huì)超出邊界。此外,數(shù)組還有一個(gè)更智能的伙伴—ArrayList,可以動(dòng)態(tài)地增長,管理對(duì)數(shù)組大小不斷變化的需求。

  C#中的數(shù)組有三種形式:一維數(shù)組、多維均勻數(shù)組(象C++中傳統(tǒng)的數(shù)組那樣)、非均勻數(shù)組(數(shù)組的數(shù)組)。我們可以通過下面的代碼創(chuàng)建一維數(shù)組:

int[]myIntArray=newint[5];


  另外,還可以以如下的方式對(duì)它進(jìn)行初始化:

int[]myIntArray={2,4,6,8,10};


  我們可以通過如下方式創(chuàng)建一個(gè)4×3的均勻數(shù)組:

int[,]myRectangularArray=newint[rows,columns];


  我們可以按如下方式對(duì)該數(shù)組進(jìn)行初始化:

int[,]myRectangularArray=
{
{0,1,2},{3,4,5},{6,7,8},{9,10,11}
};


  由于非均勻數(shù)組是數(shù)組的數(shù)組,因此,我們只能創(chuàng)建一維非均勻數(shù)組:

int[][]myJaggedArray=newint[4][];


  然后再創(chuàng)建內(nèi)部的每個(gè)數(shù)組:

myJaggedArray[0]=newint[5];
myJaggedArray[1]=newint[2];
myJaggedArray[2]=newint[3];
myJaggedArray[3]=newint[5];


  由于數(shù)組是由繼承System.Array對(duì)象而得到的,因此,它們帶有許多包括Sort、Reverse在內(nèi)的許多有用的方法。

  索引器

  我們可以創(chuàng)建象數(shù)組一樣的對(duì)象。例如,我們可以創(chuàng)建一個(gè)顯示一系列字符串的列表框,可以把列表框當(dāng)作一個(gè)數(shù)組,使用一個(gè)索引就可以很方便地訪問列表框中的內(nèi)容。

stringtheFirstString=myListBox[0];
stringtheLastString=myListBox[Length-1];


  這是通過索引器完成的。索引器在很大程度上象一個(gè)屬性,但支持索引操作的語法。圖4顯示了一個(gè)后面跟著索引操作符的屬性,圖5顯示如何完成一個(gè)很簡單的ListBox類并對(duì)它進(jìn)行索引:

  界面

  軟件界面是二種對(duì)象之間如何進(jìn)行交互的契約。如果一個(gè)對(duì)象發(fā)布了一個(gè)界面,就等于向所有可能的客戶聲明:我支持下面的方法、屬性、事件和索引器。

  C#是一種面向?qū)ο蟮恼Z言,因此這些契約被封裝在一個(gè)被稱作界面的實(shí)體中,界面定義了封裝著契約的引用型類型的對(duì)象。從概念上來講,界面與抽象類非常相似,二者的區(qū)別是抽象類可以作為一系列衍生類的基礎(chǔ)類,界面則是與其他繼承樹結(jié)合在一起的。

  IEnumerable界面

  再回到上面的例子中。象在普通的數(shù)組中那樣,使用foreach-loop循環(huán)結(jié)構(gòu)就能夠很好地打印ListBoxTest類中的字符串,通過在類中實(shí)現(xiàn)IEnumerable界面就能實(shí)現(xiàn),這是由foreach-loop循環(huán)結(jié)構(gòu)隱性地完成的。在任何支持枚舉和foreach-loop循環(huán)的類中都可以實(shí)現(xiàn)IEnumerable界面。

  IEnumerable界面只有一個(gè)方法GetEnumerator,其任務(wù)是返回一個(gè)特別的IEnumerator的實(shí)現(xiàn)。從語法的角度來看,Enumerable類能夠提供一個(gè)IEnumerator。

Figure5ListBoxClass
usingSystem;
//簡化的ListBox控制
publicclassListBoxTest
{
//用字符串初始化該ListBox
publicListBoxTest(paramsstring[]initialStrings)
{
//為字符串分配空間
myStrings=newString[256];
//把字符串拷貝到構(gòu)造器中
foreach(stringsininitialStrings)
{
myStrings[myCtr++]=s;
}
}
//在ListBox的末尾添加一個(gè)字符串
publicvoidAdd(stringtheString)
{
myStrings[myCtr++]=theString;
}
publicstringthis[intindex]
{
get
{
if(index<0||index>=myStrings.Length)
{
//處理有問題的索引
}
returnmyStrings[index];
}
set
{
myStrings[index]=value;
}
}
//返回有多少個(gè)字符串
publicintGetNumEntries()
{
returnmyCtr;
}
privatestring[]myStrings;
privateintmyCtr=0;
}
publicclassTester
{
staticvoidMain()
{
//創(chuàng)建一個(gè)新的列表并初始化
ListBoxTestlbt=newListBoxTest("Hello","World");
//添加一些新字符串
lbt.Add("Who");
lbt.Add("Is");
lbt.Add("John");
lbt.Add("Galt");
stringsubst="Universe";
lbt[1]=subst;
//訪問所有的字符串
for(inti=0;i<lbt.GetNumEntries();i++)
{
Console.WriteLine("lbt[{0}]:{1}",i,lbt[i]);
}
}
}


  Enumerator必須實(shí)現(xiàn)IEnumerator方法,這可以直接通過一個(gè)容器類或一個(gè)獨(dú)立的類實(shí)現(xiàn),后一種方法經(jīng)常被選用,因?yàn)樗梢詫⑦@一任務(wù)封裝在Enumerator類中,而不會(huì)使容器類顯得很混亂。我們將在上面代碼中的ListBoxTest中添加Enumerator類,由于Enumerator類是針對(duì)我們的容器類的(因?yàn)長istBoxEnumerator必須清楚ListBoxTest的許多情況),我們將使它在ListBoxTest中成為不公開的。在本例中,ListBoxTest被定義來完成IEnumerable界面,IEnumerable界面必須返回一個(gè)Enumerator。

publicIEnumeratorGetEnumerator()
{
return(IEnumerator)newListBoxEnumerator(this);
}


  注意,方法將當(dāng)前的ListBoxTest對(duì)象(this)傳遞給Enumerator,這將使Enumerator枚舉這一指定的ListBoxTest對(duì)象中的元素。

  實(shí)現(xiàn)這一類的Enumerator在這里被實(shí)現(xiàn)為ListBoxEnumerator,它在ListBoxTest中被定義成一個(gè)私有類,這一工作是相當(dāng)簡單的。

  被枚舉的ListBoxTest作為一個(gè)參數(shù)被傳遞給constructor,ListBoxTest被賦給變量myLBT,構(gòu)造器還會(huì)將成員變量index設(shè)置為-1,表明對(duì)象的枚舉還沒有開始。

publicListBoxEnumerator(ListBoxTesttheLB)
{
myLBT=theLB;
index=-1;
}


  MoveNext方法對(duì)index進(jìn)行加1的操作,然后確保沒有超過枚舉的對(duì)象的邊界。如果超過邊界了,就會(huì)返回false值,否則返回true值。

publicboolMoveNext()
{
index++;
if(index>=myLBT.myStrings.Length)
returnfalse;
else
returntrue;
}


  Reset的作用僅僅是將index的值設(shè)置為-1。

  Current返回最近添加的字符串,這是一個(gè)任意的設(shè)定,在其他類中,Current可以有設(shè)計(jì)人員確定的意義。無論是如何設(shè)計(jì)的,每個(gè)進(jìn)行枚舉的方法必須能夠返回當(dāng)前的成員。

publicobjectCurrent
{
get
{
return(myLBT[index]);
}
}


  對(duì)foreach循環(huán)結(jié)構(gòu)的調(diào)用能夠獲取枚舉的方法,并用它處理數(shù)組中的每個(gè)成員。由于foreach循環(huán)結(jié)構(gòu)將顯示每一個(gè)字符串,而無論我們是否添加了一個(gè)有意義的值,我們將myStrings的初始化改為8個(gè)條目,以保證顯示的易于處理。

myStrings=newString[8];


  使用基本類庫

  為了更好地理解C#與C++的區(qū)別和解決問題方式的變化,我們先來看一個(gè)比較簡單的例子。我們將創(chuàng)建一個(gè)讀取文本文件的類,并在屏幕上顯示其內(nèi)容。我將把它做成多線程程序,以便在從磁盤上讀取數(shù)據(jù)時(shí)還可以做其他的工作。

  在C++中,我們可能會(huì)創(chuàng)建一個(gè)讀文件的線程和另一個(gè)做其他工作的線程,這二個(gè)線程將各自獨(dú)立地運(yùn)行,但可能會(huì)需要對(duì)它們進(jìn)行同步。在C#中,我們也可以完成同樣的工作,由于.NET框架提供了功能強(qiáng)大的異步I/O機(jī)制,在編寫線程時(shí),我們會(huì)節(jié)省不少的時(shí)間。

  異步I/O支持是內(nèi)置在CLR中的,而且?guī)缀跖c使用正常的I/O流類一樣簡單。在程序的開始,我們首先通知編譯器,我們將在程序中使用許多名字空間中的對(duì)象:

usingSystem;
usingSystem.IO;
usingSystem.Text;


  在程序中包含System,并不會(huì)自動(dòng)地包含其所有的子名字空間,必須使用using關(guān)健字明確地包含每個(gè)子名字空間。我們?cè)诶又袝?huì)用到I/O流類,因此需要包含System.IO名字空間,我們還需要System.Text名字空間支持字節(jié)流的ASCII編碼。

  由于.NET架構(gòu)為完成了大部分的工作,編寫這一程序所需的步驟相當(dāng)簡單。我們將用到Stream類的BeginRead方法,它提供異步I/O功能,將數(shù)據(jù)讀入到一個(gè)緩沖區(qū)中,當(dāng)緩沖區(qū)可以處理時(shí)調(diào)用相應(yīng)的處理程序。

  我們需要使用一個(gè)字節(jié)數(shù)組作為緩沖區(qū)和回叫方法的代理,并將這二者定義為驅(qū)動(dòng)程序類的private成員變量。

publicclassAsynchIOTester
{
privateStreaminputStream;
privatebyte[]buffer;
privateAsyncCallbackmyCallBack;


  inputStream是一個(gè)Stream類型的變量,我們將對(duì)它調(diào)用BeginRead方法。代理與成員函數(shù)的指針非常相似。代理是C#的第一類元素。

  當(dāng)緩沖區(qū)被磁盤上的文件填滿時(shí),.NET將調(diào)用被代理的方法對(duì)數(shù)據(jù)進(jìn)行處理。在等待讀取數(shù)據(jù)期間,我們可以讓計(jì)算機(jī)完成其他的工作。(在本例中是將1個(gè)整型變量由1增加到50000,但在實(shí)際的應(yīng)用程序中,我們可以讓計(jì)算機(jī)與用戶進(jìn)行交互或作其他有意義的工作。)

  本例中的代理被定義為AsyncCallback類型的過程,這是Stream的BeginRead方法所需要的。System空間中AsyncCallback類型代理的定義如下所示:

publicdelegatevoidAsyncCallback(IAsyncResultar);


  這一代理可以是與任何返回void類型值、將IAsyncResult界面作為參數(shù)的方法相關(guān)聯(lián)的。在該方法被調(diào)用時(shí),CLR可以在運(yùn)行時(shí)傳遞IAsyncResult界面對(duì)象作為參數(shù)。我們需要如下所示的形式定義該方法:

voidOnCompletedRead(IAsyncResultasyncResult)


  然后在構(gòu)造器中與代理連接起來:

AsynchIOTester()
{
???
myCallBack=newAsyncCallback(this.OnCompletedRead);
}


  上面的代碼將代理的實(shí)例賦給成員變量myCallback。下面是全部程序的詳細(xì)工作原理。在Main函數(shù)中,創(chuàng)建了一個(gè)類的實(shí)例,并讓它開始運(yùn)行:

publicstaticvoidMain()
{
AsynchIOTestertheApp=newAsynchIOTester();
theApp.Run();
}


  new關(guān)健字能夠啟動(dòng)構(gòu)造器。在構(gòu)造器中我們打開一個(gè)文件,并得到一個(gè)Stream對(duì)象。然后在緩沖中分配空間并與回調(diào)機(jī)制聯(lián)結(jié)起來。

AsynchIOTester()
{
inputStream=File.OpenRead(@"C:\MSDN\fromCppToCS.txt");
buffer=newbyte[BUFFER_SIZE];
myCallBack=newAsyncCallback(this.OnCompletedRead);
}


  在Run方法中,我們調(diào)用了BeginRead,它將以異步的方式讀取文件。

inputStream.BeginRead(
buffer,//存放結(jié)果
0,//偏移量
buffer.Length,//緩沖區(qū)中有多少字節(jié)
myCallBack,//回調(diào)代理
null);//本地對(duì)象


  這時(shí),我們可以完成其他的工作。

for(longi=0;i<50000;i++)
{
if(i%1000==0)
{
Console.WriteLine("i:{0}",i);
}
}


  文件讀取操作結(jié)束后,CLR將調(diào)用回調(diào)方法。

voidOnCompletedRead(IAsyncResultasyncResult)
{


  在OnCompletedRead中要做的第一件事就是通過調(diào)用Stream對(duì)象的EndRead方法找出讀取了多少字節(jié):

intbytesRead=inputStream.EndRead(asyncResult);


  對(duì)EndRead的調(diào)用將返回讀取的字節(jié)數(shù)。如果返回的數(shù)字比0大,則將緩沖區(qū)轉(zhuǎn)換為一個(gè)字符串,然后將它寫到控制臺(tái)上,然后再次調(diào)用BeginRead,開始另一次異步讀的過程。

if(bytesRead>0)
{
Strings=Encoding.ASCII.GetString(buffer,0,bytesRead);
Console.WriteLine(s);
inputStream.BeginRead(buffer,0,buffer.Length,
myCallBack,null);
}


  現(xiàn)在,在讀取文件的過程中就可以作別的工作了(在本例中是從1數(shù)到50000),但我們可以在每次緩沖區(qū)滿了時(shí)對(duì)讀取的數(shù)據(jù)進(jìn)行處理(在本例中是向控制臺(tái)輸出緩沖區(qū)中的數(shù)據(jù))。有興趣的讀者可以點(diǎn)擊此處下載完整的源代碼。

  異步I/O的管理完全是由CLR提供的,這樣,在網(wǎng)絡(luò)上讀取文件時(shí),會(huì)更好些。

  在網(wǎng)絡(luò)上讀取文件

  在C++中,在網(wǎng)絡(luò)上讀取文件需要有相當(dāng)?shù)木幊碳记桑?NET對(duì)此提供了廣泛的支持。事實(shí)上,在網(wǎng)絡(luò)上讀取文件僅僅是基礎(chǔ)類庫中Stream類的另一種應(yīng)用。

  首先,為了對(duì)TCP/IP端口(在本例中是65000)進(jìn)行監(jiān)聽,我們需要?jiǎng)?chuàng)建一個(gè)TCPListener類的實(shí)例。

TCPListenertcpListener=newTCPListener(65000);


  一旦創(chuàng)建后,就讓它開始進(jìn)行監(jiān)聽。

tcpListener.Start();


  現(xiàn)在就要等待客戶連接的要求了。

SocketsocketForClient=tcpListener.Accept();


  TCPListener對(duì)象的Accept方法返回一個(gè)Socket對(duì)象,Accept是一個(gè)同步的方法,除非接收到一個(gè)連接請(qǐng)求它才會(huì)返回。如果連接成功,就可以開始向客戶發(fā)送文件了。

if(socketForClient.Connected)
{
???


  接下來,我們需要?jiǎng)?chuàng)建一個(gè)NetworkStream類,將報(bào)路傳遞給constructor:

NetworkStreamnetworkStream=newNetworkStream(socketForClient);


  然后創(chuàng)建一個(gè)StreamWriter對(duì)象,只是這次不是在文件上而是在剛才創(chuàng)建的NetworkStream類上創(chuàng)建該對(duì)象:

System.IO.StreamWriterstreamWriter=
newSystem.IO.StreamWriter(networkStream);


  當(dāng)向該流寫內(nèi)容時(shí),流就通過網(wǎng)絡(luò)被傳輸給客戶端。

  客戶端的創(chuàng)建

  客戶端軟件就是一個(gè)TCPClient類的具體例子,TCPClient類代表連向主機(jī)的一個(gè)TCP/IP連接。

TCPClientsocketForServer;
socketForServer=newTCPClient("localHost",65000);


  有了TCPClient對(duì)象后,我們就可以創(chuàng)建NetworkStream對(duì)象了,然后在其上創(chuàng)建StreamReader類:

NetworkStreamnetworkStream=socketForServer.GetStream();
System.IO.StreamReaderstreamReader=
newSystem.IO.StreamReader(networkStream);


  現(xiàn)在,只要其中有數(shù)據(jù)就讀取該流,并將結(jié)果輸出到控制臺(tái)上。

do
{
outputString=streamReader.ReadLine();

if(outputString!=null)
{
Console.WriteLine(outputString);
}
}
while(outputString!=null);


  為了對(duì)這一段代碼進(jìn)行測(cè)試,可以創(chuàng)建如下一個(gè)測(cè)試用的文件:

Thisislineone
Thisislinetwo
Thisislinethree
Thisislinefour


  這是來自服務(wù)器的輸出:

Output(Server)
Clientconnected
SendingThisislineone
SendingThisislinetwo
SendingThisislinethree
SendingThisislinefour
Disconnectingfromclient...
Exiting...


  下面是來自客戶端的輸出:

Thisislineone
Thisislinetwo
Thisislinethree
Thisislinefour


  屬性和元數(shù)據(jù)

  C#和C++之間一個(gè)顯著的區(qū)別是它提供了對(duì)元數(shù)據(jù)的支持:有關(guān)類、對(duì)象、方法等其他實(shí)體的數(shù)據(jù)。屬性可以分為二類:一類以CLR的一部分的形式出現(xiàn),另一種是我們自己創(chuàng)建的屬性,CLR屬性用來支持串行化、排列和COM協(xié)同性等。一些屬性是針對(duì)一個(gè)組合體的,有些屬性則是針對(duì)類或界面,它們也被稱作是屬性目標(biāo)。

  將屬性放在屬性目標(biāo)前的方括號(hào)內(nèi),屬性就可以作用于它們的屬性目標(biāo)。

[assembly:AssemblyDelaySign(false)]
[assembly:AssemblyKeyFile(".\\keyFile.snk")]


  或用逗號(hào)將各個(gè)屬性分開:

[assembly:AssemblyDelaySign(false),
assembly:AssemblyKeyFile(".\\keyFile.snk")]


  自定義的屬性

  我們可以任意創(chuàng)建自定義屬性,并在認(rèn)為合適的時(shí)候使用它們。假設(shè)我們需要跟蹤bug的修復(fù)情況,就需要建立一個(gè)包含bug的數(shù)據(jù)庫,但需要將bug報(bào)告與專門的修正情況綁定在一塊兒,則可能在代碼中添加如下所示的注釋:

//Bug323fixedbyJesseLiberty1/1/2005.


  這樣,在源代碼中就可以一目了然地了解bug的修正情況,但如果如果把相關(guān)的資料保存在數(shù)據(jù)庫中可能會(huì)更好,這樣就更方便我們的查詢工作了。如果所有的bug報(bào)告都使用相同的語法那就更好了,但這時(shí)我們就需要一個(gè)定制的屬性了。我們可能使用下面的內(nèi)容代替代碼中的注釋:

[BugFix(323,"JesseLiberty","1/1/2005")Comment="Offbyoneerror"]


  與C#中的其他元素一樣,屬性也是類。定制化的屬性類需要繼承System.Attribute:

publicclassBugFixAttribute:System.Attribute

  我們需要讓編譯器知道這個(gè)屬性可以跟什么類型的元素,我們可以通過如下的方式來指定該類型的元素:

[AttributeUsage(AttributeTargets.ClassMembers,AllowMultiple=true)]


  AttributeUsage是一個(gè)作用于屬性的屬性━━元屬性,它提供的是元數(shù)據(jù)的元數(shù)據(jù),也即有關(guān)元數(shù)據(jù)的數(shù)據(jù)。在這種情況下,我們需要傳遞二個(gè)參數(shù),第一個(gè)是目標(biāo)(在本例中是類成員。),第二個(gè)是表示一個(gè)給定的元素是否可以接受多于一個(gè)屬性的標(biāo)記。AllowMultiple的值被設(shè)置為true,意味著類成員可以有多于一個(gè)BugFixAttribute屬性。如果要聯(lián)合二個(gè)屬性目標(biāo),可以使用OR操作符連接它們。

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface,AllowMultiple=true)]


  上面的代碼將使一個(gè)屬性隸屬于一個(gè)類或一個(gè)界面。

  新的自定義屬性被命名為BugFixAttribute。命名的規(guī)則是在屬性名之后添加Attribute。在將屬性指派給一個(gè)元素后,編譯器允許我們使用精簡的屬性名調(diào)用這一屬性。因此,下面的代碼是合法的:

[BugFix(123,"JesseLiberty","01/01/05",Comment="Offbyone")]


  編譯器將首先查找名字為BugFix的屬性,如果沒有發(fā)現(xiàn),則查找BugFixAttribute。

  每個(gè)屬性必須至少有一個(gè)構(gòu)造器。屬性可以接受二種類型的參數(shù):環(huán)境參數(shù)和命名參數(shù)。在前面的例子中,bugID、編程人員的名字和日期是環(huán)境參數(shù),注釋是命名參數(shù)。環(huán)境參數(shù)被傳遞到構(gòu)造器中的,而且必須按在構(gòu)造器中定義的順序傳遞。

publicBugFixAttribute(intbugID,stringprogrammer,stringdate)
{
this.bugID=bugID;
this.programmer=programmer;
this.date=date;
}

Namedparametersareimplementedasproperties.


  屬性的使用

  為了對(duì)屬性進(jìn)行測(cè)試,我們創(chuàng)建一個(gè)名字為MyMath的簡單類,并給它添加二個(gè)函數(shù),然后給它指定bugfix屬性。

[BugFixAttribute(121,"JesseLiberty","01/03/05")]

[BugFixAttribute(107,"JesseLiberty","01/04/05",
Comment="Fixedoffbyoneerrors")]
publicclassMyMath


  這些數(shù)據(jù)將與元數(shù)據(jù)存儲(chǔ)在一起。下面是完整的源代碼及其輸出:

  自定義屬性

usingSystem;
//創(chuàng)建被指派給類成員的自定義屬性
[AttributeUsage(AttributeTargets.Class,
AllowMultiple=true)]
publicclassBugFixAttribute:System.Attribute
{
//位置參數(shù)的自定義屬性構(gòu)造器
publicBugFixAttribute
(intbugID,
stringprogrammer,
stringdate)
{
this.bugID=bugID;
this.programmer=programmer;
this.date=date;
}
publicintBugID
{
get
{
returnbugID;
}
}

//命名參數(shù)的屬性
publicstringComment
{
get
{
returncomment;
}
set
{
comment=value;
}
}

publicstringDate
{
get
{
returndate;
}
}

publicstringProgrammer
{
get
{
returnprogrammer;
}
}

//專有成員數(shù)據(jù)
privateintbugID;
privatestringcomment;
privatestringdate;
privatestringprogrammer;
}

//把屬性指派給類

[BugFixAttribute(121,"JesseLiberty","01/03/05")]
[BugFixAttribute(107,"JesseLiberty","01/04/05",
Comment="Fixedoffbyoneerrors")]
publicclassMyMath
{

publicdoubleDoFunc1(doubleparam1)
{
returnparam1+DoFunc2(param1);
}

publicdoubleDoFunc2(doubleparam1)
{
returnparam1/3;
}

}

publicclassTester
{
publicstaticvoidMain()
{
MyMathmm=newMyMath();
Console.WriteLine("CallingDoFunc(7).Result:{0}",
mm.DoFunc1(7));
}
}


輸出:

CallingDoFunc(7).Result:9.3333333333333339


  象我們看到的那樣,屬性對(duì)輸出絕對(duì)沒有影響,創(chuàng)建屬性也不會(huì)影響代碼的性能。到目前為止,讀者也只是在聽我論述有關(guān)屬性的問題,使用ILDASM瀏覽元數(shù)據(jù),就會(huì)發(fā)現(xiàn)屬性確實(shí)是存在的。

  映射

  在許多情況下,我們需要一種方法,能夠從元數(shù)據(jù)中訪問屬性,C#提供了對(duì)映射的支持以訪問元數(shù)據(jù)。通過初始化MemberInfo類型對(duì)象,System.Reflection名字空間中的這個(gè)對(duì)象可以用來發(fā)現(xiàn)成員的屬性,對(duì)元數(shù)據(jù)進(jìn)行訪問。

System.Reflection.MemberInfoinf=typeof(MyMath);


  對(duì)MyMath類型調(diào)用typeof操作符,它返回一個(gè)由繼承MemberInfo而生成的Type類型的變量。

  下一步是對(duì)MemberInfo對(duì)象調(diào)用GetCustomAttributes,并將希望得到的屬性的類型作為一個(gè)參數(shù)傳遞給GetCustomAttributes。我們將得到一個(gè)對(duì)象數(shù)組,數(shù)組的每個(gè)成員的類型都是BugFixAttribute。

object[]attributes;
attributes=Attribute.GetCustomAttributes(inf,typeof(BugFixAttribute));


  我們就可以遍歷這個(gè)數(shù)組了,打印BugFixAttribute對(duì)象的數(shù)組,代碼下所示:

  屬性的打印

publicstaticvoidMain()
{
MyMathmm=newMyMath();
Console.WriteLine("CallingDoFunc(7).Result:{0}",
mm.DoFunc1(7));

//獲取成員信息并使用它訪問自定義的屬性
System.Reflection.MemberInfoinf=typeof(MyMath);
object[]attributes;
attributes=
Attribute.GetCustomAttributes(inf,typeof(BugFixAttribute));

//遍歷所有的屬性
foreach(Objectattributeinattributes)
{
BugFixAttributebfa=(BugFixAttribute)attribute;
Console.WriteLine("\nBugID:{0}",bfa.BugID);
Console.WriteLine("Programmer:{0}",bfa.Programmer);
Console.WriteLine("Date:{0}",bfa.Date);
Console.WriteLine("Comment:{0}",bfa.Comment);
}
}


  類型發(fā)現(xiàn)

  我們可以通過映象的方法來研究一個(gè)組合實(shí)體的內(nèi)容,如果要建立需要顯示組合體內(nèi)部信息的工具或動(dòng)態(tài)地調(diào)用組合體中的途徑,這一方法是非常有用的。

  通過映象的方法,我們可以知道一個(gè)模塊、方法、域、屬性的類型,以及該類型的每個(gè)方法的信號(hào)、該類支持的界面和該類的超級(jí)類。我們可以通過如下的形式,用Assembly.Load靜態(tài)方法動(dòng)態(tài)地加載一個(gè)組合體:

publicstaticAssembly.Load(AssemblyName)


  然后,可以將它傳遞到核心庫中。

Assemblya=Assembly.Load("Mscorlib.dll");


  一旦加載了組合體,我們可以通過調(diào)用GetTypes返回一個(gè)Type對(duì)象數(shù)組。Type對(duì)象是映射的核心,它表示類、界面、數(shù)組、值和枚舉等的類型定義。

Type[]types=a.GetTypes();


  組合休會(huì)返回一個(gè)類型的數(shù)組,我們可以使用foreach-loop結(jié)構(gòu)顯示該數(shù)組,其輸出將有好幾頁文檔之多,下面我們從中找一小段:

TypeisSystem.TypeCode
TypeisSystem.Security.Util.StringExpressionSet
TypeisSystem.Text.UTF7Encoding$Encoder
TypeisSystem.ArgIterator
TypeisSystem.Runtime.Remoting.JITLookupTable
1205typesfound


  我們得到了一個(gè)內(nèi)容為核心庫中類型的數(shù)組,可以將它們都打印出來,該數(shù)組將有1205個(gè)項(xiàng)。

  對(duì)一種類型映射我們也可以對(duì)組合體中一種類型進(jìn)行映射。為此,我們可以使用GetType方法從組合體中解析出一個(gè)類型:

publicclassTester
{
publicstaticvoidMain()
{
//檢查一個(gè)對(duì)象
TypetheType=Type.GetType("System.Reflection.Assembly");
Console.WriteLine("\nSingleTypeis{0}\n",theType);
}
}


  輸出如下所示:

SingleTypeisSystem.Reflection.Assembly


  發(fā)現(xiàn)成員

  我們還可以得到所有成員的類型,顯示所有的方法、屬性、域,下面的代碼演示了實(shí)現(xiàn)上述目標(biāo)的代碼。

Figure9GettingAllMembers
publicclassTester
{
publicstaticvoidMain()
{
//檢查一個(gè)單一的對(duì)象
TypetheType=Type.GetType("System.Reflection.Assembly");
Console.WriteLine("\nSingleTypeis{0}\n",theType);

//獲取所有的成員
MemberInfo[]mbrInfoArray=
theType.GetMembers(BindingFlags.LookupAll);
foreach(MemberInfombrInfoinmbrInfoArray)
{
Console.WriteLine("{0}isa{1}",
mbrInfo,mbrInfo.MemberType.Format());
}
}
}


  盡管得到的輸出還非常長,但在輸出中我們可以得到如下面的不甘落后民示的域、方法、構(gòu)造器和屬性:

System.Strings_localFilePrefixisaField
BooleanIsDefined(System.Type)isaMethod
Void.ctor()isaConstructor
System.StringCodeBaseisaProperty
System.StringCopiedCodeBaseisaProperty


  只發(fā)現(xiàn)方法

  我們可能會(huì)只關(guān)心方法,而不關(guān)心域、屬性等,為此,我們需要?jiǎng)h除如下的對(duì)GetMembers的調(diào)用:

MemberInfo[]mbrInfoArray=
theType.GetMembers(BindingFlags.LookupAll);


  然后添加調(diào)用GetMethods的語句:

mbrInfoArray=theType.GetMethods();


  現(xiàn)在,輸出中就只剩下方法了。

Output(excerpt)
BooleanEquals(System.Object)isaMethod
System.StringToString()isaMethod
System.StringCreateQualifiedName(System.String,System.String)
isaMethod
System.Reflection.MethodInfoget_EntryPoint()isaMethod


  發(fā)現(xiàn)特定的成員

  最后,為了進(jìn)一步地縮小范圍,我們可以使用FindMembers方法來發(fā)現(xiàn)某一類型的特定的方法。例如,在下面的代碼中,我們可以只搜索以"Get"開頭的方法。

publicclassTester
{
publicstaticvoidMain()
{
//檢查一個(gè)單一的對(duì)象
TypetheType=Type.GetType("System.Reflection.Assembly");
//只獲取以Get開頭的成員
MemberInfo[]mbrInfoArray
theType.FindMembers(MemberTypes.Method,
BindingFlags.Default,
Type.FilterName,"Get*");
foreach(MemberInfombrInfoinmbrInfoArray)
{
Console.WriteLine("{0}isa{1}",
mbrInfo,mbrInfo.MemberType.Format());
}
}
}


  其輸出的一部分如下所示:

System.Type[]GetTypes()isaMethod
System.Type[]GetExportedTypes()isaMethod
System.TypeGetType(System.String,Boolean)isaMethod
System.TypeGetType(System.String)isaMethod
System.Reflection.AssemblyNameGetName(Boolean)isaMethod
System.Reflection.AssemblyNameGetName()isaMethod
Int32GetHashCode()isaMethod
System.Reflection.AssemblyGetAssembly(System.Type)isaMethod
System.TypeGetType(System.String,Boolean,Boolean)isaMethod


  動(dòng)態(tài)調(diào)用

  一旦發(fā)現(xiàn)一個(gè)方法,可以使用映射的方法調(diào)用它。例如,我們可能需要調(diào)用System.Math中的Cos方法(返回一個(gè)角的余弦值)。為此,我們需要獲得System.Math類的類型信息,如下所示:

TypetheMathType=Type.GetType("System.Math");


  有了類型信息,我們就可以動(dòng)態(tài)地加載一個(gè)類的實(shí)例:

ObjecttheObj=Activator.CreateInstance(theMathType);


  CreateInstance是Activator類的一個(gè)靜態(tài)方法,可以用來對(duì)對(duì)象進(jìn)行初始化。

  有了System.Math類的實(shí)例后,我們就可以調(diào)用Cos方法了。我們還需要準(zhǔn)備好一個(gè)定義參數(shù)類型的數(shù)組,因?yàn)镃os只需要一個(gè)參數(shù)(需要求余弦值的角度),因此數(shù)組中只需要有一個(gè)成員。我們將在數(shù)組中賦予一個(gè)System.Double類型的Type對(duì)象,也就是Cos方法需要的參數(shù)的類型:

Type[]paramTypes=newType[1];
paramTypes[0]=Type.GetType("System.Double");


  現(xiàn)在我們就可以傳遞方法的名字了,這個(gè)數(shù)組定義了Type對(duì)象中GetMethod方法的參數(shù)的類型:

MethodInfoCosineInfo=
theMathType.GetMethod("Cos",paramTypes);


  我們現(xiàn)在得到了MethodInfo類型的對(duì)象,我們可以在其上調(diào)用相應(yīng)的方法。為此,我們需要再次在數(shù)組中傳入?yún)?shù)的實(shí)際值:

Object[]parameters=newObject[1];
parameters[0]=45;
ObjectreturnVal=CosineInfo.Invoke(theObj,parameters);


  需要注意的是,我創(chuàng)建了二個(gè)數(shù)組,第一個(gè)名字為paramTypes的數(shù)組存儲(chǔ)著參數(shù)的類型,第二個(gè)名字為parameters的數(shù)組保存實(shí)際的參數(shù)值。如果方法需要二個(gè)參數(shù),我們就需要使這二個(gè)數(shù)組每個(gè)保持二個(gè)參數(shù)。如果方法不需要參數(shù),我們?nèi)匀恍枰獎(jiǎng)?chuàng)建這二個(gè)數(shù)組,只是無需在里面存儲(chǔ)數(shù)據(jù)即可。

Type[]paramTypes=newType[0];


  盡管看起來有點(diǎn)奇怪,但它是正確的。下面是完整的代碼:

  映射方法的使用

usingSystem;
usingSystem.Reflection;publicclassTester
{
publicstaticvoidMain()
{
TypetheMathType=Type.GetType("System.Math");
ObjecttheObj=Activator.CreateInstance(theMathType);

//只有一個(gè)成員的數(shù)組
Type[]paramTypes=newType[1];
paramTypes[0]=Type.GetType("System.Double");

//獲得Cos()方法的信息
MethodInfoCosineInfo=
theMathType.GetMethod("Cos",paramTypes);

//將實(shí)際的參數(shù)填寫在一個(gè)數(shù)組中
Object[]parameters=newObject[1];
parameters[0]=45;
ObjectreturnVal=CosineInfo.Invoke(theObj,parameters);
Console.WriteLine(
"Thecosineofa45degreeangle{0}",returnVal);

}
}


  結(jié)論

  盡管有許多小錯(cuò)誤等著C++編程人員去犯,但C#的語法與C++并沒有太大的不同,向新語言的轉(zhuǎn)換是相當(dāng)容易的。使用C#的有趣的部分是使用通用語言運(yùn)行庫,這篇文章只能涉及幾個(gè)重點(diǎn)問題。CLR和.NETFramework提供了對(duì)線程、集合、互聯(lián)網(wǎng)應(yīng)用開發(fā)、基于Windows的應(yīng)用開發(fā)等方面提供了更多的支持。語言功能和CLR功能之間的區(qū)分是非常模糊的,但組合在一起就是一種功能非常強(qiáng)大的開發(fā)工具了。

關(guān)鍵詞:C#