查看单个帖子
旧 2008-10-27, 11:34 AM   #6
huangyhg
超级版主
 
huangyhg的头像
 
注册日期: 04-03
帖子: 18592
精华: 36
现金: 249466 标准币
资产: 1080358888 标准币
huangyhg 向着好的方向发展
默认 回复: 【转帖】c#的语法与c++非常相似,有些变化引起的错误却很严重,本讨论c++编程人员最容易犯的十个错误。

using System.Drawing;

class Tester

{

public static void Main()

{

using (Font theFont = new Font("Arial", 10.0f))

{

//使用theFont对象

} // 编译器将调用Dispose处理theFont对象

Font anotherFont = new Font("Courier",12.0f);

using (anotherFont)

{

// 使用anotherFont对象

} // 编译器将调用Dispose处理anotherFont对象 }

}




   在本例的第一部分中,Font对象是在Using语句中创建的。当Using语句结束时,系统就会调用Dispose,对Font对象进行处理。在本例的第二部分,Font对象是在Using语句外部创建的,在决定使用它时,再将它放在Using语句内,当Using语句结束时,系统就会调用Dispose。Using语句还能防止其他意外的发生,保证系统一定会调用Dispose。

 错误3:C#中的值型变量和引用型变量是有区别的

 与C++一样,C#也是一种强类型编程语言。C#中的数据类型被分为了二大类:C#语言本身所固有的数据类型和用户自定义数据类型,这一点也与C++相似。

 此外,C#语言还把变量分为值类型和引用类型。除非是被包含在一个引用类型中,值类型变量的值保留在栈中,这一点与C++中的变量非常相似。引用类型的变量也是栈的一种,它的值是堆中对象的地址,与C++中的指针非常地相似。值类型变量的值被直接传递给方法,引用型变量在被作为参数传递给方法时,传递的是索引。类和界面可以创建引用类变量,但需要指出的是,结构数据类型是C#的一种内置数据类型,同时也是一种值型的数据类型。

 错误4:注意隐性的数据类型转换

 Boxing和unboxing是使值型数据类型被当作索引型数据类型使用的二个过程。值型变量可以被包装进一个对象中,然后再被解包回值型变量。包括内置数据类型在内的所有C#中的数据类型都可以被隐性地转化为一个对象。包装一个值型变量就会生成一个对象的实例,然后将变量拷贝到实例中。

 Boxing是隐性的,如果在需要索引型数据类型的地方使用了值型数据类型的变量,值型变量就会隐性地转化为索引型数据类型的变量。Boxing会影响代码执行的性能,因此应当尽量避免,尤其是在数据量较大的时候。

 如果要将一个打包的对象转换回原来的值型变量,必须显性地对它进行解包。解包需要二个步骤:首先对对象实例进行检查,确保它们是由值型的变量被包装成的;第二步将实例中的值拷贝到值型变量中。为了确保解包成功,被解包的对象必须是通过打包一个值型变量的值生成的对象的索引。

using System;

public class UnboxingTest

{

public static void Main()

{

int i = 123; //打包

object o = i; // 解包(必须是显性的)

int j = (int) o;

Console.WriteLine("j: {0}", j); }

}



 如果被解包的对象是无效的,或是一个不同数据类型对象的索引,就会产生InvalidCastException异外。

 错误5:结构与对象是有区别的

 C++中的结构与类差不多,唯一的区别是,在缺省状态下,结构的访问权限是public,其继承权限也是public。一些C++编程人员将结构作为数据对象,但这只是一个约定而非是必须这样的。在C#中,结构只是一个用户自定义的数据类型,并不能取代类。尽管结构也支持属性、方法、域和操作符,但不支持继承和destructor。

 更重要的是,类是一种索引型数据类型,结构是值型数据类型。因此,结构在表达无需索引操作的对象方面更有用。结构在数组操作方面的效率更高,而在集合的操作方面则效率较低。集合需要索引,结构必须打包才适合在集合的操作中使用,类在较大规模的集合操作中的效率更高。

 错误6:虚方法必须被明确地覆盖

 在C#语言中,编程人员在覆盖一个虚方法时必须显性地使用override关健字。假设一个Window类是由A公司编写的,ListBox和RadioButton类是由B公司的和编程人员在购买的A公司编写的Window类的基础上编写的,B公司的编程人员对包括Window类未来的变化情况在内的设计知之甚少。如果B公司的一位编程人员要在ListBox上添加一个Sort方法:

public class ListBox : Window

{ public virtual void Sort() {"}

}



 在A公司发布新版的Window类之前,这不会有任何问题。如果A公司的编程人员也在Window类中添加了一个Sort方法。

public class Window

{ // " public virtual void Sort() {"}

}



 在C++中,Windows类中的Sort方法将成为ListBox类中Sort方法的基础方法,在希望调用Windows类中的Sort方法时,ListBox类中的Sort方法就会被调用。在C#中,虚拟函数总是被认为是虚拟调度的根。也就是说,一旦C#发现一个虚拟的方法,就不会再在虚拟链中查找其他虚拟方法。如果ListBox再次被编译,编译器就会生成一个警告信息:

"\class1.cs(54,24): warning CS0114: 'ListBox.Sort()' hides

inherited member 'Window.Sort()'.



 要使当前的成员覆盖原来的方法,就需要添加override关健字,或者添加new关健字。

 要消除警告信息,编程人员必须搞清楚他想干什么。可以在ListBox类中的Sort方法前添加new,表明它不应该覆盖Window中的虚方法:

public class ListBox : Window {

public new virtual void Sort() {"}



 这样就可以清除警告信息。如果编程人员确实希望覆盖掉Window中的方法,就必须使用override关健字来显性地表明其意图。

 错误7:类成员变量的初始化

 C#中的初始化与C++中不同。假设有一个带有private性质的成员变量age的Person类,Employee是由继承Person类而生成的,它有一个private性质的salaryLevel成员变量。在C++中,我们可以在Employee的构造器的初始化部分初始化salaryLevel,如下面的代码所示:

Employee::Employee(int theAge, int theSalaryLevel):

Person(theAge) // 初始化基础类

salaryLevel(theSalaryLevel) // 初始化成员变量

{

// 构造器的代码

}



 这种方法在C#中是非法的。尽管仍然可以初始化基础类,但象上面的代码那样对成员变量初始化就会引起编译错误。在C#中,我们可以在定义成员变量时的同时对它进行初始化:

Class Employee : public Person

{ // 成员变量的定义

private salaryLevel = 3; // 初始化

}



 注意:必须明确地定义每个变量的访问权限。

 错误8:布尔型变量与整型变量是两回事儿

 if( someFuncWhichReturnsAValue() )

 在C#中,布尔型变量与整型变量并不相同,因此下面的代码是不正确的:

 if( someFuncWhichReturnsAValue() )

 if someFuncWhichReturnsAValue返回零表示false,否则表示true的想法已经行不通了。这样的好处是原来存在的将赋值运算与相等相混淆的错误就不会再犯了。因此下面的代码:

 if ( x = 5 )

 在编译时就会出错,因为x=5只是把5赋给了X,而不是一个布尔值。

 错误9:switch语句中会有些语句执行不到

 在C#中,如果一个switch语句执行了一些操作,则程序就可能不能执行到下一个语句。因此,尽管下面的代码在C++中是合法的,但在C#中却不合法:

switch (i)

{

case 4:

CallFuncOne(); case 5: // 错误,不会执行到这里

CallSomeFunc();

}



 要实现上面代码的目的,需要使用一个goto语句:

 switch (i)

{

case 4: CallFuncOne();

goto case 5; case 5:

CallSomeFunc();

}



 如果case语句不执行任何代码,则所有的语句都会被执行。如下面的代码:

switch (i)

{

case 4: // 能执行到 case 5: // 能执行到

case 6: CallSomeFunc();

}



 错误10:C#中的变量要求明确地赋值

 在C#中,所有的变量在使用前都必须被赋值。因此,可以在定义变量时不对它进行初始化,如果在把它传递给一个方法前,必须被赋值。

 如果只是通过索引向方法传递一个变量,并且该变量是方法的输出变量,这是就会带来问题。例如,假设有一个方法,它返回当前时间的小时、分、秒,如果象下面这样编写代码:

int theHour;

int theMinute;

int theSecond;

timeObject.GetTime( ref theHour, ref theMinute, ref theSecond)



 如果在使用theHour、theMinute和theSecond这三个变量之前没有对它们进行初始化,就会产生一个编译错误:

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参数修饰符,它可以使一个参数无需初始化就可以被引用。例如,GetTime中的参数对它本身没有一点意义,它们只是为了表达该方法的输出。在方法中返回之前,Out参数中必须被指定一个值。下面是经过修改后的GetTime方法:

public void GetTime(out int h, out int m, out int s) {

h = Hour;

m = Minute;

s = Second;

}



  下面是新的GetTime方法的调用方法:

timeObject.GetTime( out theHour, out theMinute, out theSecond);



上面文章引自http://www.uml.org.cn/c%2B%2B/200605081.htm
__________________
借用达朗贝尔的名言:前进吧,你会得到信心!
[url="http://www.dimcax.com"]几何尺寸与公差标准[/url]
huangyhg离线中   回复时引用此帖