如果被解包的對象是無效的,或是一個不同數(shù)據(jù)類型對象的索引,就會產(chǎn)生InvalidCastException異外。
錯誤5:結(jié)構(gòu)與對象是有區(qū)別的
C++中的結(jié)構(gòu)與類差不多,唯一的區(qū)別是,在缺省狀態(tài)下,結(jié)構(gòu)的訪問權(quán)限是public,其繼承權(quán)限也是public。一些C++編程人員將結(jié)構(gòu)作為數(shù)據(jù)對象,但這只是一個約定而非是必須這樣的。
在C#中,結(jié)構(gòu)只是一個用戶自定義的數(shù)據(jù)類型,并不能取代類。盡管結(jié)構(gòu)也支持屬性、方法、域和操作符,但不支持繼承和destructor。
更重要的是,類是一種索引型數(shù)據(jù)類型,結(jié)構(gòu)是值型數(shù)據(jù)類型。因此,結(jié)構(gòu)在表達(dá)無需索引操作的對象方面更有用。結(jié)構(gòu)在數(shù)組操作方面的效率更高,而在集合的操作方面則效率較低。集合需要索引,結(jié)構(gòu)必須打包才適合在集合的操作中使用,類在較大規(guī)模的集合操作中的效率更高。
錯誤6:虛方法必須被明確地覆蓋
在C#語言中,編程人員在覆蓋一個虛方法時必須顯性地使用override關(guān)健字。假設(shè)一個Window類是由A公司編寫的,ListBox和RadioButton類是由B公司的和編程人員在購買的A公司編寫的Window類的基礎(chǔ)上編寫的,B公司的編程人員對包括Window類未來的變化情況在內(nèi)的設(shè)計知之甚少。
如果B公司的一位編程人員要在ListBox上添加一個Sort方法:
public class ListBox : Window
{
public virtual void Sort() {"}
}
在A公司發(fā)布新版的Window類之前,這不會有任何問題。如果A公司的編程人員也在Window類中添加了一個Sort方法。
public class Window
{
// "
public virtual void Sort() {"}
}
在C++中,Windows類中的Sort方法將成為ListBox類中Sort方法的基礎(chǔ)方法,在希望調(diào)用Windows類中的Sort方法時,ListBox類中的Sort方法就會被調(diào)用。在C#中,虛擬函數(shù)總是被認(rèn)為是虛擬調(diào)度的根。也就是說,一旦C#發(fā)現(xiàn)一個虛擬的方法,就不會再在虛擬鏈中查找其他虛擬方法。如果ListBox再次被編譯,編譯器就會生成一個警告信息:
"\class1.cs(54,24): warning CS0114: 'ListBox.Sort()' hides
inherited member 'Window.Sort()'.
要使當(dāng)前的成員覆蓋原來的方法,就需要添加override關(guān)健字,或者添加new關(guān)健字。
要消除警告信息,編程人員必須搞清楚他想干什么??梢栽贚istBox類中的Sort方法前添加new,表明它不應(yīng)該覆蓋Window中的虛方法:
public class ListBox : Window
{
public new virtual void Sort() {"}
這樣就可以清除警告信息。如果編程人員確實希望覆蓋掉Window中的方法,就必須使用override關(guān)健字來顯性地表明其意圖。
錯誤7:類成員變量的初始化
C#中的初始化與C++中不同。假設(shè)有一個帶有private性質(zhì)的成員變量age的Person類,Employee是由繼承Person類而生成的,它有一個private性質(zhì)的salaryLevel成員變量。在C++中,我們可以在Employee的構(gòu)造器的初始化部分初始化salaryLevel,如下面的代碼所示:
Employee::Employee(int theAge, int theSalaryLevel):
Person(theAge) // 初始化基礎(chǔ)類
salaryLevel(theSalaryLevel) // 初始化成員變量
{
// 構(gòu)造器的代碼
}
這種方法在C#中是非法的。盡管仍然可以初始化基礎(chǔ)類,但象上面的代碼那樣對成員變量初始化就會引起編譯錯誤。在C#中,我們可以在定義成員變量時的同時對它進(jìn)行初始化:
Class Employee : public Person
{
// 成員變量的定義
private salaryLevel = 3; // 初始化
}
注意:必須明確地定義每個變量的訪問權(quán)限。
錯誤8:布爾型變量與整型變量是兩回事兒
if( someFuncWhichReturnsAValue() )
在C#中,布爾型變量與整型變量并不相同,因此下面的代碼是不正確的:
if( someFuncWhichReturnsAValue() )
if someFuncWhichReturnsAValue返回零表示false,否則表示true的想法已經(jīng)行不通了。這樣的好處是原來存在的將賦值運算與相等相混淆的錯誤就不會再犯了。因此下面的代碼:
if ( x = 5 )
在編譯時就會出錯,因為x=5只是把5賦給了X,而不是一個布爾值。
錯誤9:switch語句中會有些語句執(zhí)行不到
在C#中,如果一個switch語句執(zhí)行了一些操作,則程序就可能不能執(zhí)行到下一個語句。因此,盡管下面的代碼在C++中是合法的,但在C#中卻不合法:
switch (i)
{
case 4:
CallFuncOne();
case 5: // 錯誤,不會執(zhí)行到這里
CallSomeFunc();
}
要實現(xiàn)上面代碼的目的,需要使用一個goto語句:
switch (i)
{
case 4:
CallFuncOne();
goto case 5;
case 5:
CallSomeFunc();
}
如果case語句不執(zhí)行任何代碼,則所有的語句都會被執(zhí)行。如下面的代碼:
switch (i)
{
case 4: // 能執(zhí)行到
case 5: // 能執(zhí)行到
case 6:
CallSomeFunc();
}
錯誤10:C#中的變量要求明確地賦值
在C#中,所有的變量在使用前都必須被賦值。因此,可以在定義變量時不對它進(jìn)行初始化,如果在把它傳遞給一個方法前,必須被賦值。
如果只是通過索引向方法傳遞一個變量,并且該變量是方法的輸出變量,這是就會帶來問題。例如,假設(shè)有一個方法,它返回當(dāng)前時間的小時、分、秒,如果象下面這樣編寫代碼:
int theHour;
int theMinute;
int theSecond;
timeObject.GetTime( ref theHour, ref theMinute, ref theSecond)
如果在使用theHour、theMinute和theSecond這三個變量之前沒有對它們進(jìn)行初始化,就會產(chǎn)生一個編譯錯誤:
Use of unassigned local variable 'theHour'
Use of unassigned local variable 'theMinute'
Use of unassigned local variable 'theSecond'
我們可以通過將這些變量初始化為0或其他對方法的返回值沒有影響的值,以解決編譯器的這個小問題:
int theHour = 0;
int theMinute = 0;
int theSecond = 0;
timeObject.GetTime( ref theHour, ref theMinute, ref theSecond)
這樣就有些太麻煩了,這些變量傳遞給GetTime方法,然后被改變而已。為了解決這一問題,C#專門針對這一情況提供了out參數(shù)修飾符,它可以使一個參數(shù)無需初始化就可以被引用。例如,GetTime中的參數(shù)對它本身沒有一點意義,它們只是為了表達(dá)該方法的輸出。在方法中返回之前,Out參數(shù)中必須被指定一個值。下面是經(jīng)過修改后的GetTime方法:
public void GetTime(out int h, out int m, out int s)
{
h = Hour;
m = Minute;
s = Second;
}
下面是新的GetTime方法的調(diào)用方法:
timeObject.GetTime( out theHour, out theMinute, out theSecond);