c#实验九.docx
《c#实验九.docx》由会员分享,可在线阅读,更多相关《c#实验九.docx(14页珍藏版)》请在冰豆网上搜索。
![c#实验九.docx](https://file1.bdocx.com/fileroot1/2022-11/26/0bb7eadb-f2ad-4e57-9cc7-8a412357326d/0bb7eadb-f2ad-4e57-9cc7-8a412357326d1.gif)
c#实验九
实验九类的继承
实验目的
1.了解继承在类的设计中的优点
2.掌握利用继承设计简单的类
3.掌握类继承中,成员的关系
实验要求:
掌握类的继承的相关概念,能够设计简单的继承类以及能够够理解继承类的数据成员关系等。
实验步骤
1父类和子类
现实中,有很多类别存在“一般”和“特殊”的关系。
比如,人类和动物类就具有特殊和一般的关系。
我们称比较特殊的那个类为“子类”,而另一个是“父类”。
父类和子类是相对而言的。
练习1:
1)汽车、交通工具、飞机
2)运动用品、球类、篮球、足球
写出以下类中具有父类和子类关系的类:
1父类:
交通工具
子类:
汽车、飞机
2父类:
运动用品
子类:
球类、篮球、足球
3父类:
球类
子类:
篮球、足球
2使用继承进行类的设计
对于具有父子关系的类,首先设计父类,将父类和子类共有的成员放在父类中。
然后,派生出子类,并将特殊的成员放在子类中。
包含继承的类的一般定义语法是:
class类名:
基类
{
类体
}
例1:
usingSystem;
classParentClass
{
publicintx;
publicvoidprint()
{
Console.WriteLine("Hello");
}
}
classChildClass:
ParentClass
{
privateinty;
}
classHello
{
publicstaticvoidMain()
{
ChildClasschild=newChildClass();
child.print();//继承得到的方法
child.x=10;//继承得到的字段
Console.WriteLine(2*child.x);
}
}
在例中,子类ChildClass中并没有定义成员方法print()和成员字段x,但是它继承于类ParentClass,因此也自动具有ParentClass类的成员方法print()和成员字段x,也具有父类所有的数据和功能(包括成员字段x和成员方法print())。
练习2:
usingSystem;
classA
{
publicvoidPrint(){Console.WriteLine("IaminClassA");}
}
classB:
A
{
publicvoidHello(){Console.WriteLine("Hello");}
}
classTest
{
publicstaticvoidMain()
{
Aa=newA();
Bb=newB();
a.Print();
b.Print();
b.Hello();
}
}
上述程序是否正确?
如果正确,程序的运行输出结果是什么?
正确
IaminClassA
IaminClassA
Hello
练习3:
请说出以下程序中,对象b1的成员有哪些?
其中哪些可以通过对象名直接访问?
那些不能通过对象名直接访问?
答:
x,y,z,w.直接访问:
x,w不能通过对象名直接:
y,z.
usingSystem;
classA
{
publicintx;
privateinty;
}
classB:
A
{
publicintz;
privateintw;
}
classTest
{
publicstaticvoidMain()
{
Bb1=newB();
b1._______=100;
}
}
3继承的类的构造函数:
usingSystem;
publicclassPC
{
publicPC()
{
Console.WriteLine("父类构造函数");
}
publicvoidprint()
{
Console.WriteLine("父类中的print方法");
}
}
publicclassCC:
PC
{
publicCC()
{
Console.WriteLine("子类的构造函数");
}
}
Classprogram
{
publicstaticvoidMain()
{
CCchild=newCC();
child.print();
}
}
运行结果是:
父类构造函数
子类的构造函数
父类中的print方法
可见,当父类也有构造函数时,生成子类对象也会自动调用父类的构造函数,而且在子类的构造函数之前调用。
练习4:
指出以下程序的运行结果:
答:
300
classA
{
publicintx=100;
publicA(){x=x+50;}
publicintgetX(){returnx;}
}
classC:
A
{
publicC(){x=x*2;}
}
classProgram
{
staticvoidMain()
{
Cc1=newC();
System.Console.WriteLine(c1.getX());
}
}
如果要指定父类构造函数的调用版本(默认情况调用无参的父类构造函数),需要在子类的构造函数中用base关键字指定。
例
usingSystem;
publicclassParent
{
stringparentString;
publicParent()
{
Console.WriteLine("Parent类的无参构造函数");
}
publicParent(stringmyString)
{
parentString=myString;
Console.WriteLine("Parent类中带字符串参数{0}的构造函数",parentString);
}
publicvoidprint()
{
Console.WriteLine("Parent类中的print()");
}
}
publicclassChild:
Parent
{
publicChild():
base("东方不败")
{
Console.WriteLine("Child类的构造函数");
}
publicvoidprint()
{
base.print();
Console.WriteLine("Child类中的print()");
}
publicstaticvoidMain()
{
Childchild=newChild();
child.print();
}
}
如果没有程序中红字部分,则生成child对象时,会调用父类中的无参构造函数。
但是,使用了base("东方不败")之后,可以指定调用父类中的另一个版本(也就是带一个字符串参数)的构造函数。
上例中的输出结果是:
Parent类中带字符串参数东方不败的构造函数
Child类的构造函数
Parent类中的print()
Child类中的print()
练习5:
指出以下程序的运行结果50,120
usingSystem;
publicclassA
{
intx;
publicA()
{
x=50;
}
publicA(intx0)
{
x=100+x0;
}
publicintgetX()
{
returnx;
}
}
publicclassB:
A
{
publicB()
{
}
publicB(intx0):
base(2*x0)
{
}
}
classProgram
{
publicstaticvoidMain()
{
Bb1=newB();
Console.WriteLine(b1.getX());
Bb2=newB(10);
Console.WriteLine(b2.getX());
}
}
4在子类中对方法进行重定义:
父类中已经定义的方法,子类中也可以重新定义。
例:
usingSystem;
publicclassParent
{
stringparentString;
publicvoidprint()
{
Console.WriteLine("父类中的print()");
}
}
publicclassChild:
Parent
{
publicvoidprint()
{
Console.WriteLine("子类中的print()");
}
publicstaticvoidMain()
{
Childchild=newChild();
child.print();
}
}
运行结果是:
子类中的print()
在该例中,父类Parent中定义了方法print(),子类中也定义了同名的方法(包括参数类型也相同)。
上例中,child.print()方法会调用子类(即Child类)中的方法。
在子类的成员中访问被重定义的方法,默认访问的是子类中的方法。
例:
usingSystem;
classParent
{
stringparentString;
publicvoidprint()
{
Console.WriteLine("父类中的print.");
}
}
classChild:
Parent
{
publicvoidprint()
{
Console.WriteLine("子类中的print.");
}
publicvoidtest()
{
print();
//父子类中都存在print()方法,在子类中调用print()时默认调用的是子类中的版本
}
}
classTest
{
publicstaticvoidMain()
{
Childchild=newChild();
child.test();
}
}
上例中,程序运行结果是:
子类中的print.
如果在子类的方法内要指定访问父类中的方法,可以使用base.父类方法名()的方法。
例:
usingSystem;
classParent
{
stringparentString;
publicvoidprint()
{
Console.WriteLine("父类中的print.");
}
}
classChild:
Parent
{
publicvoidprint()
{
Console.WriteLine("子类中的print.");
}
publicvoidtest()
{
base.print();//子类中,在方法前加上base,可以调用父类中的方法
}
}
classTest
{
publicstaticvoidMain()
{
Childchild=newChild();
child.test();
}
}
运行结果:
父类中的print.
该例的类Child是类Parent的子类。
在子类Child的方法test()中如果直接调用print(),则调用的是子类中的版本。
但如果在test()方法中通过base.print()的方式访问,则能够访问到父类中的同名方法。
练习6:
利用继承设计两个类:
Weapon(武器)和Tank(坦克)。
Weapon具有重量、火力、价格、抗击打力等数据成员
Tank除了具有Weapon所有成员之外,还具有单位里程耗油量、移动速度、健康程度等。
当Tank受到打击的时候,会受到损伤,健康程度减少量=打击力度/抗击打力。
Tank具有方法BeHit,参数为受到打击的力度
Tank具有方法ShowState,显示数据成员的数值。
模拟以下过程:
生成一个Tank对象,具有初始值:
重量为2000,火力800,价格500000,抗击打力600,健康程度为100。
对象受到火力为20000的打击。
显示受打击后对象的状态。
usingSystem;
publicclassWeapon
{
publicdoubleWeight;
publicdoubleHit;
publicdoublePrice;
publicdoubleDenyHit;
publicWeapon(doubleWeight,doubleHit,doublePrice,doubleDenyHit)
{
this.Weight=Weight;
this.Hit=Hit;
this.Price=Price;
this.DenyHit=DenyHit;
}
}
publicclassTank:
Weapon
{
publicdoubleoil;
publicdoubledrive;
publicinthealthy;
publicintBehit(doubleHit)
{
returnhealthy-=(int)(Hit/DenyHit);
}
publicTank(doubleWeight,doubleHit,doublePrice,doubleDenyHit,doubleoil,doubledrive,inthealthy)
:
base(Weight,Hit,Price,DenyHit)
{
this.oil=oil;
this.drive=drive;
this.healthy=healthy;
}
publicvoidShowState()
{
Console.WriteLine("坦克的重量是{0},火力是{1},坦克价格是{2},抗击力是{3},受打后的健康是{4},耗油为{5}每小时,速度是{6}米每秒",Weight,Hit,Price,DenyHit,healthy,oil,drive);
}
}
classTest
{
publicstaticvoidMain()
{
Tankt=newTank(2000,800,500000,600,100,10,10);
t.Behit(20000);
t.oil=30;
t.drive=600;
t.ShowState();
}
}
本次课程的所有练习要求同学们在第十三周之内完成,需要交实验报告和源程序,将源代码打包,并命名为“学号+姓名”,上传到作业ftp。