深入浅出C#中文版 图文皆译第六章.docx

上传人:b****8 文档编号:11177662 上传时间:2023-02-25 格式:DOCX 页数:51 大小:2.98MB
下载 相关 举报
深入浅出C#中文版 图文皆译第六章.docx_第1页
第1页 / 共51页
深入浅出C#中文版 图文皆译第六章.docx_第2页
第2页 / 共51页
深入浅出C#中文版 图文皆译第六章.docx_第3页
第3页 / 共51页
深入浅出C#中文版 图文皆译第六章.docx_第4页
第4页 / 共51页
深入浅出C#中文版 图文皆译第六章.docx_第5页
第5页 / 共51页
点击查看更多>>
下载资源
资源描述

深入浅出C#中文版 图文皆译第六章.docx

《深入浅出C#中文版 图文皆译第六章.docx》由会员分享,可在线阅读,更多相关《深入浅出C#中文版 图文皆译第六章.docx(51页珍藏版)》请在冰豆网上搜索。

深入浅出C#中文版 图文皆译第六章.docx

深入浅出C#中文版图文皆译第六章

有时你真的想要像你的父母。

曾经遇到过可以做你想要它做的事情的对象吗?

曾经希望过你只要只要修改一点东西对象就变得完美了吗?

这就是C#语言中继承如此重要的原因。

读了这一章,你将学会如何通过继承一个对象来获得它的行为,同时还保持修改它的行为的灵活性。

你将不用再复制代码,可以更好的模拟现实世界,最后写出容易维护的代码。

(下载源码就到源码网:

凯瑟琳也承接生日聚会

你的程序已经可以工作了,凯瑟琳总是在用它。

但是她不只是承晚宴聚会--她现在也做生日聚会,而且计费方式与晚宴聚会有所不同。

她需要你给程序添加生日聚会的功能。

我们需要一个BirthDayParty类

修改你的程序来计算生日聚会的花费就意味着要添加一个类并改变窗体来同时掌管两种类型的聚会。

一、创建一个新的BirthDayParty类

这个类要计算花费,处理装饰,并检查蛋糕上的字体的大小。

二、添加一个TabControl控件到窗体上。

窗体上的每一个tab就像第三章上显示Joe和Bob有多少现金的GroupBox控件。

点击你想要显示的tab,并向其中拖拽控件。

三、把晚宴聚会需要的控件拖拽到第一个tab上去

要把每一个处理晚宴聚会的空间都拖拽到上面去。

它们将会像原来一样的工作,但是它们只有在晚宴聚会的tab被选中时才显示出来。

四、向第二个tab上添加新的生日聚会的控件

你需要像设计晚宴聚会的界面一样的来设计生日聚会的界面。

五、根据控件来写生日聚会的类

现在只需要在窗体的字段里面添加一个BirthDayParty的引用,并向新的控件添加代码以使得它们可以使用类的方法和属性。

问:

为什么不可以像Mike想要在导航器中比较三条道路时一样,直接创建一个DinnerParty的新实例呢?

答:

因为如果你创建了DinnerParty的另一个新实例,它只能用来计划另一个晚宴聚会。

两个同类的实例只有在你需要处理同类的两份数据的时候才会显得有用。

但是如果你需要存储不同的数据,那你就需要不同的类来完成。

问:

我怎么知道该往新类里面写些什么呢?

答:

开始构建爱一个类之前,需要先知道它是用来解决什么问题的。

因此你必须和凯瑟琳谈谈—是她要使用这个程序。

还好你记了不少笔记!

你可以通过考虑类的行为(类需要做什么)和状态(类需要知悉什么)来想出类的方法,字段,和属性。

四.构建生日聚会的界面

生日聚会的GUI含有一个NumbericUpDown控件来代表人数,含有一个CheckBox控件来代表高级装饰,一个有着3D边界的标签来显示总花费。

然后还有一个TextBox控件来显示蛋糕上要写的字。

五.你将需要这个属性

这是BirthDayParty.CakeWriting属性的代码--它会派上用场的:

六.把它们组合起来

所有的部分都写好了,现在只需要写一些代码来让控件工作。

*添加一个BirthDayParty对象到窗体。

确保你实例化了它。

*编写NumbericUpDown控件的事件处理方法,该方法用来设置NumberOfPeople属性。

*让高级装饰的CheckBox可以工作

*添加一个DisplayBirthDayPartyCost()方法,并在所有的事件处理方法中调用它,这样显示花费的标签将会在任何变化发生的时候得以更新。

七.运行

确保程序以它被预期的方式运行。

检查一下文字过长的时候是不是会弹出错误提示框。

确保价钱总是正确的。

上面这些做好了,你的工作就搞定了!

向凯瑟琳的聚会计划程序添加生日聚会

给凯瑟琳的聚会计划程序添加生日聚会

括号对于单行的代码块来说是可选的

很多时候你的if语句或者while循环下面的代码块中都会只有一句语句。

如果有很多这样的if和while,也就会有很多的括号--它看起来很不顺眼!

C#允许你在代码块中只有一句语句的情况下省略掉括号,这样就解决了问题。

所以下面的代码绝对符合语法:

另一件事儿...你可以给超过12人的聚会额外收费$100吗?

用了你的程序,凯瑟琳接到了很多的用户,她可以跟某些大客户多收一点费用了。

那,要怎么做才能让你的程序有多收费一点的功能呢?

*修改DinnerParty.CalculateCost()方法来让它检查NumberOfPeople,如果人数多于12,就给返回值加上$100。

*用同样的方式修改BirthDayParty.CalculateCost()方法。

想一下怎么能够一下子就让DinnerParty和BirthDayParty同时具有多收费的功能呢。

要写设么代码?

写在哪儿呢?

这个问题很简单...但是如果有三个类似的类呢?

四个呢?

十多个呢?

如果你还要维护这些类,有可能要做更多的更改呢?

如果要对五六个相似的类做同样的更改需要怎么做呢?

对!

在多个类中写同样的代码是低效率的,而且容易出错。

很幸运,C#给我们构建相互关联的、有共同行为的类提供了更好的方法:

继承。

给你的类应用继承,你的代码就只需要写一次了

DinnerParty和BirthDayParty两个类中有很多相似代码并不是巧合。

用C#写程序的时候,总是会创建代表现实事物的类--而现实中的事物总是相关联的。

你的类中有相似代码是因为它们代表的现实事物--生日聚会和晚宴聚会--有着相似的行为。

晚宴聚会和生日聚会都是聚会

如果有两个类都是某种概括性事物的特例,你可以让它们都去继承同一个类。

这样,它们就都是同一个基类的子类。

从概括到具体的构建你的类模型

C#程序使用继承,因为它模拟你要构建的现实事物之间的关系。

现实事物总是处于由概括到具体的层级中,你的程序也有自己的类层级。

你的类模型中,下面层级中的类继承于上面层级中的类。

(下载源码就到源码网:

继承,动词

从祖先或者父母那儿取得一种特性。

她想让孩子继承自己的褐色大眼睛,而不是她丈夫的蓝色小圆眼。

你会怎样设计动物园模拟器呢?

狮子、老虎、熊...哈哈!

还有河马、狼,或许还有猫。

你要设计一个动物园模拟程序。

(别太兴奋了--我们不是真的要去写代码,只是设计动物们的类。

我们有一个要写到程序里面去的动物的列表,但是并不全。

我们知道每个动物都需要有一个对象代表,对象将会在模拟器中移动,做它被预先编程的事情。

更重要的是,我们的程序要让别的程序员容易维护,也就是说别的程序员将要能够在需要添加新动物到模拟器的时候就能够添加新的类进去。

第一步做什么呢?

嗯,先不说具体的某种动物,先弄清楚各种动物有什么共同的特性,所有动物的抽象的特性。

然后构建一个所有动物类都可以继承的,有着这些特性的类。

①找出所有动物的共同特性

看看这六个动物。

狮子、河马、老虎、猫、达尔马西亚狗,它们有什么共同点呢?

它们有什么联系?

你要弄清楚它们的关系然后才能构建出包含所有特性的类模型。

使用继承来防止子类中的冗余代码

你已经知道了冗余代码很糟糕。

它难以维护,很是让人头疼。

我们来选择要写在Animal类中的只需要写一次的字段和方法,这样每一个动物的子类都可以从其中继承。

我们从公有字段开始吧:

*Picture:

可以放进PictureBox控件的图像。

*Food:

该种动物要吃的食物种类。

现在,只有两种食物类型值:

肉或者草。

*Hunger:

一个代表饥饿程度的int。

它的改变取决于动物什么时候进食了和吃了多少。

*Boundaries:

存储笼子高度、宽度、位置的类的一个应用。

动物会在这个笼子中转悠。

*Location:

动物所在地的X、Y坐标。

Animal类还有四个方法可以供子类继承:

*MakeNoise():

让动物发声的方法。

*Eat();动物吃它喜爱的食物的行为。

*Sleep();让动物躺下并小睡的方法。

*Roam():

动物喜欢在笼子里面转悠。

②创建一个基类来给动物们提供共同的特性

基类中的字段、属性、和方法将会给所有继承自它的子类们共同的状态和行为。

子类们都是动物,所以基类叫做Animal是说的通的。

不同的动物出声也不同

狮吼、犬吠,据我们所知河马不出声。

每一个Animal的子类都有一个MakeNoise()方法,但是它们的工作方式不同,代码也不同。

当一个子类要改变继承来的某个行为的时候,这就叫做覆盖方法。

(下载源码就到源码网:

③搞明白每个Animal子类是怎样不同的实现基类的行为--或者根本不实现

每种动物都做什么别的动物不做的事儿呢?

狗吃狗粮,所以狗的Eat()方法需要覆盖Aniaml.Eat()方法。

河马会游泳,所以河马会有一个基类中根本不存在的Swim()方法。

想想要覆盖什么

子类改变继承来的行为就叫做覆盖。

每个都需要进食。

狗吃少量的肉,河马吃大量的草。

那么它们的行为的代码是什么样的呢?

狗和河马都要覆盖Eat()方法。

河马的方法每次被调用将会消耗,大概,二十磅干草。

狗的Eat()方法将会消耗动物园的食品供给中的十二盎司狗粮。

 

想一想怎么给动物们分组

AgedVermount奶酪是一种奶酪,奶酪是一种乳制品,乳制品是一种食物,一个好的类模型要可以表现出这点来。

很幸运,C#给我们一种简单的方式来实现这一点。

你可以创建一系列互相继承的类,自顶向下的继承。

你可以有一个Food类,它有一个子类叫做DairyProduct,而DairyProduct又是Cheese的基类,Cheese又有一个叫做Cheddar的子类,AgedVermountCheddar就是继承自Cheddar。

④寻找有很多共同点的类

狼和狗是不是看起来很相似?

它们都是犬科动物,去看看它们的行为,肯定有很多的共同点。

它们有可能吃一样的食物,睡觉的方式也相似。

家猫,狮子和老虎又怎么样呢?

事实是它们都以相同的方式绕着自己的栖息地转悠。

在Animal和这三个猫科动物的类之间有一个Feline(猫科动物)类肯定会对于避免冗余代码有帮助。

 

创建类层次结构

创建很多类,最上面有一个基类,下面是子类,子类们还有自己的子类,这样就是创建了类层次结构。

这不仅可以防止冗余代码,也是一个很明智的层次结构。

但是最大的好处就是这可以让你的代码易懂,且容易维护。

看动物园模拟器的时候,如果看到Feline类中定义了一些属性和方法,你就可以立即意识到你看见的是所有猫科动物共享的东西。

你的类层次结构就像帮你游历代码的地图。

⑤完成你的类层次结构

现在你知道了如何组织动物类,你可以添加Feline和Canine类了。

每个子类都扩展它的基类

你并不会被局限于一个子类继承来的方法...你已经知道这一点了!

毕竟你已经自己构建类很久了。

向一个类添加继承就是把已经创建好的类拿过来,并通过添加属性,字段,方法来扩展它。

所以如果你要给狗添加一个Fetch()方法,这是很普通的。

这不会继承或者覆盖任何东西--只有狗才有这个方法,河马,狼,犬科动物,动物等等类都不会得到这个方法。

C#总是会调用最具体的方法

如果你让狗对象转悠(Roam()方法),只有一个方法可以做到--就是Animal中的版本。

那要是要让狗出声音呢?

调用的是哪个MakeNoise()方法呢?

要搞明白这一点并不难。

Dog类中的方法告诉你狗是怎么做这件事儿的。

如果这个方法是在Canine中的话,那就是告诉你所有的犬科动物都是这样做这件事儿的。

如果它在Animal中的话,那就是描述了一个普遍性的、对于所有动物都适用的行为。

所以如果你让狗出声音的话,C#会先去Dog类中找适用于狗的这种行为。

如果Dog类没有这种行为,就去Canine中找,然后再去Animal中找。

继承基类要用冒号

写一个类的时候,继承另一个类要用冒号(:

)。

冒号前面的就是子类,它会得到父类所有的字段,属性,方法。

子类继承基类的时候,基类所有的字段,属性,方法会自动的添加进子类中去。

问:

为什么子类和父类之间的箭头指向父类?

如果箭头向下指的话,这个类图不是会好看一点吗?

答:

是会好看一点,但是就不如之前准确了。

一个类继承另一个类的时候,这种关系是体现在子类中的,父类是保持不变的。

从父类的角度考虑,这样是有道理的,给一个类添加一个子类,父类的行为是不会改变的。

父类甚至到不会知晓有一个新的子类存在。

父类的方法,字段,和属性都是原封不动的。

而子类的行为却改变了。

每一个子类的实例都会得到父类所有的字段,方法和属性,而这一切,只写一个冒号就做到了。

在类图上这样画箭头就代表父类会成为子类的一部分,而箭头指向子类继承的父类。

看看这些类模型和声明,然后圈出错误的语句。

看看这些类模型和声明,然后圈出错误的语句。

 

我们知道继承会把基类的属性,方法,字段添加到子类中去

如果子类需要父类所有的字段,方法,属性的话,继承是很简单的。

...但是有的鸟不会飞!

如果父类有一个在子类中需要被改变的方法,那怎么办呢?

子类可以通过覆盖来改变或者替换继承来的方法

有时候你会想要让子类继承父类的大部分行为,而不是全部。

你可以用继承改变一个类继承来的方法。

①给父类中的方法添加virtual关键字

子类可以覆盖在父类中用virtual关键字标记的方法,virtual关键字标记的方法表示可以被子类覆盖。

②给子类添加一个同名的方法

需要用相同的方法签名--也就是说相同的返回值和参数--而且还需要在声明中用override关键字。

在子类中用以override关键字修饰的方法来替代从父类中继承来的方法。

在此之前,父类中的方法需要用virtual关键字修饰。

可以使用父类的地方,都可以用子类来替代父类

继承最有用的方面就是可用子类来替代父类。

所以,如果Recipe()方法接受一个Cheese类型的参数,而有一个叫做AgedVermontCheddar的类继承自Cheese,你可以把AgedVermontCheddar的实例传进该方法作为参数。

Recipe()方法只可以访问Cheese中定义的属性,方法,字段,它不可以访问AgedVermontCheddar中的独有的成员。

①假设我们有一个分析三明治对象的方法

②你可以给上面的方法传递一个三明治对象--不过你也可以传递一个BLT对象,因为我们让BLT继承了Sandwich类,BLT也是一种三明治了。

③类图可以向下看--也就是说父类的引用变量可以被赋值为子类的实例。

而反之则不然。

简介:

1.填写代码中的空白处

2.让填进去的候选代码和输出相匹配

下面是一段C#程序。

代码中遗失了一块!

你要把候选的代码块(左侧列出的那些)和相应的输出联系起来,也就是说填入哪段代码块将要在消息框中弹出哪段输出呢?

并不是所有的输出都要用到,而且有的输出不止要用一次。

画线把候选代码块和输出结果连起来。

(不要把它写进IDE去试--在纸上做出它来,你会学到更多!

泳池难题

你的任务是把游泳池中的代码片段拿出来填进代码中的空白处。

可以多次使用同一个代码块,也有的代码块可能不会用到。

你的目标就是让这些类可以编译并以运行。

别被表面现象骗了--这个难题比它看起来要难。

问:

你在泳池难题中指出了程序入口点--这是不是意味着我可以写一个没有Form1窗体的程序?

答:

对。

创建窗体应用的时候,IDE会帮你创建项目需要的所有文件,这包括一个Program.cs(内有一个含有程序入口点的静态类),还有Form1.cs(内含一个叫做Form1的空白窗体)。

试试这样做:

用IDE创建新项目的时候不要选择窗体项目,选择空项目。

然后向其中添加一个类文件,把泳池难题中的代码写进去。

由于这个程序要用消息框,你需要通过添加引用来引入“System.Windows.Forms”(而创建窗体应用的时候,这一个步骤将会有IDE帮你自动完成)。

最后,在项目菜单中选择属性,并把项目类型设置为窗体应用。

现在运行...你就可以看见结果了!

恭喜,你徒手创建了一个C#程序。

问:

可以继承含有入口点的类吗?

答:

可以。

入口点必须是静态方法,但是它并不一定要处于一个静态类中。

(记住,用static修饰的类不可以被实例化,但是它的方法在程序一开始就可用了。

所以在泳池难题的程序中,你可以在任何其他方法中调用TestBoats.Main(),而无须声明引用变量或者用new关键字实例化对象。

问:

我搞不明白为什么有些方法叫做“虚方法”--它们看起来很真实感啊!

答:

virtual关键字关系到.Net在后台如何处理虚方法。

.net会使用一种叫做虚方法表的东西。

.NET用它记录哪些方法是虚方法,哪些方法被覆写过。

别担心--你无需知道它具体怎么工作也可以使用虚方法!

问:

你说类图中只可以向上移不可以向下移是什么意思?

答:

在类图中,上层的类比下层的类更具抽象性。

比较具体的类(例如衬衫,汽车)继承自比较抽象的类(例如衣物,机动车)。

这样想,就很容易明白当你只是需要随便一种机动车的时候汽车,小货车,摩托车都可以胜任。

但是如果你需要汽车,摩托车对你就没有用了。

继承就如上面说的那样。

如果有一个方法,接受Vehicle类型的参数,而Motorcycle继承自Vehicle,这样你就可以把一个Motorcycle的实例传进这个方法中去。

但是如果接受的参数是Motorcycle类型,就不是随便传进一个Vehicle类型去都行了,因为传进去的有可能是Van(小货车)的实例,而这样如果该方法要访问Hadlebars(摩托车把)属性的话,C#就不知道怎么办好了!

对于需要一个父类类

型作参数的方法,可

以传递一个子类的实

例进去。

子类可以通过使用base关键字访问基类

即使你已经覆写了父类的方法、属性,但是有时你还是会需要访问父类。

很幸运,我们可以用base关键字来访问父类的任何方法。

①所有动物都要吃东西,所以Animal类有一个Eat()方法,它接受一个Food对象作为参数。

②变色龙用舌头捕食。

所以Chameleon(变色龙)类会继承Animal类并覆写Eat()。

③无需写重复的代码,我们只需使用base关键字来调用已经被覆盖的方法。

我们现在对旧的和新版本的Eat()方法都有访问权。

 

父类有构造方法,子类也要有

一个类由构造方法,那么任何继承自它的子类都会调用它的构造方法。

子类和父类的构造方法可以有不同的参数列表。

父类的构造方法首先得以调用,然后才执行子类的构造方法

但是别盲目相信我们说的--自己试试看!

①创建一个父类,让它的构造方法弹出一个消息框

然后向窗体添加一个按钮,它实例化父类并会弹出消息框:

②写一个子类,但是别调用父类的构造方法

然后向窗体添加一个按钮,让它实例化一个子类的对象并显示一个消息框:

③通过让子类的构造方法调用父类的构造方法来解决问题

然后实例化子类,看看消息框以什么顺序弹出来!

 

现在你可以去完成凯瑟琳的任务了!

你上次放手凯瑟琳的工作的时候,刚刚给她的程序添加了生日聚会的功能。

她需要你给她的程序添加一个向12人以上的聚会加收$100的功能。

当时你好像必须把一样的代码写两次,每个类一次。

现在你会用继承了,你可以让两个聚会类继承同一个父类,父类中含有两个聚会类的公共代码,这样同样的代码就不用写两次了。

如果你做的好的话,我们可以做到修改两个类而无须修改窗体!

①我们来创建新的类模型

我们还是会保留DinnerParty和BirthdayParty类,但是现在它们要继承自同一个Party类。

我们需要让它们保持和原来一样的方法、字段、属性,这样就无需修改窗体了。

但是某些方法、字段、属性需要移动到Party类中去,我们还需要覆写它们中的一部分。

 

②创建Party类

确保Party类是public的。

你需要仔细的观察类图,来分析出要把DinnerParty和BirthdayParty中哪些的方法、属性移动到Party中去。

*把NumberOfPeople和CostOfDecoration移动进去,以此来与DinnerParty和BirthdayParty保持兼容。

*也把CalculatorCostOfDecorations()和CalculateCost()方法移动进去。

如果这些方法用到了某些私有字段,也要把该字段一起移动。

(记住,子类只继承公有字段--一旦你把私有字段移动到Party类,DinnerParty和BirthdayParty类就不能访问该字段了。

*Party类还要有构造方法。

仔细观察一下DinnerParty和BirthdayParty的构造方法--它们相同的部分要移动到Party的构造方法里面去。

*现在添加向超过12人的聚会加收费$100的功能。

毕竟,我们就是因此才在做这些改变的!

这个特性同时属于DinnerParty和BirthdayParty,所以它需要处于Party类中。

③让DinnerParty继承自Party

由于现在Party类可以做很多原来DinnerParty做的工作,所以你可以把重叠部分从DinnerParty中消除掉了,只把DinnerParty中独有的部分保留下来。

*确保构造方法运行良好。

它做Party的构造方法不做的事儿吗?

如果做的话,把它保留下来,其他相同部分留给父类的构造方法去完成。

*关于健康选择的部分要留在DinnerParty中。

*你至少需要覆写一个方法,因为它做DinnerParty中以独特的方式工作。

④让BirthdayParty继承Party

对于BirthdayParty也一样,把共性的东西移动到父类,只把特性的东西保留在BirthdayParty中。

*BirthdayParty的构造方法需要做什么Party的构造方法没有完成的事情吗?

*你要在BirthdayParty内处理蛋糕花费。

它触及到一个方法和一个属性,你需要覆写它们。

*没错,可以覆写属性!

和覆写方法一样。

你设置base.NumberOfPeople的值的时候,会调用这个属性父类中的的set访问器。

使用set和get访问器都需要用到base关键字。

注意--你修改了DinnerParty和BirthdayParty类,让它们继承Party。

这样你就可以修改总花费的计算,加收$100了,而且根本不用改变窗体。

帅!

 

创建一个蜂窝管理系统

蜂后需要你的帮助!

她的蜂窝失控了,需要你写一个程序来管理蜂窝。

蜂窝里有很多工蜂,有很多工作需要做。

但是她控制不了局面了,她不知道哪个蜜蜂在做什么,她也不知道是否有足够的蜜蜂来做需要做的工作。

你要创建一个蜂窝管理系统来帮助蜂后管理工蜂。

它将如此工作:

①蜂后给工蜂分配工作

工蜂需要做六种工作。

有的工蜂懂得采集花蜜和制作蜂蜜,其他的会维护蜂窝、巡逻、防卫敌人。

有一些工蜂会做所有的工作。

所以你需要让蜂后可以给工蜂分配其可以胜任的工作。

②工作分配完成之后,就要开工了。

蜂后分配完工作之后,她点击“Workthenextshift(进行下一个半次)”按钮。

然后程序生成一个班次报告告诉蜂后哪些蜜蜂这个班次上工了,它做了什么工作,还需要多少个班次来完成一项工作。

 

首先你要构建基本系统

这个项目分为两部分。

第一部分你要创建基本系统来管理蜂窝。

这一部分有两个类,Queen(蜂后)和Worker(工蜂)。

你要为程序创建窗体,并把它与这两个类结合起来。

这两个类需要封装良好,这样在你去创建第二部分的时候它们才能易于修改。

程序含有一个Queen对象来管理工作。

*Queen用一个Worker数组来管理工蜂,看它们是否已经有分配了

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 经济学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1