零基础学会C#四——继承&抽象类和抽象方法&使用委托和事件实现模拟闹铃&多态&对象存储数据项
1、目标
- 单一继承
- 抽象类和抽象方法
- 使用委托和事件实现模拟闹铃。
- 飞机和客机类的多态程序
- Hashtable对象存储数据项
2、继承
2.1 问题
现实生活中,哺乳动物是动物界中一个分支;可以动物普遍具有的特征都可以在哺乳动物身上找到;但哺乳动物也有自己独特的习性。因此可以将动物看成是哺乳动物的父亲。编程实现上可以通过哺乳动物类继承一般动物类,从而获得一般动物的特征。
2.2 说明
用C#编写一个程序,使用Animal和Mammal两个类来说明一般动物和哺乳动物的继承关系。Animal具有名称、所属门类等属性,需要提供方法实现以接收和显示这些属性的值。
Mammal类具有代表哺乳动物习性的属性,这些属性表明哺乳动物与其他类型动物的区别。同样地,需要提供方法实现以接收和显示这些属性的值。
2.3 分析
Animal类是一个基类,它包含_name、_type两个成员以及用于接收和显示信息的三个方法。名为Mammal的派生类包含_temperature、_isViviparous两个成员和用于接收和显示信息的两个方法。
为了检验创建的Animal和Mammal类,应实例化一个Mammal类对象,并调用基类和派生类的方法来存储和检索值。
2.4 推荐步骤:
(1)新建一个名为“SingleInher”的基于控制台应用程序的项目。
(2)将以下代码添加到“Program.cs”中。
2.5 代码
using System;
using System.Collections.Generic;
using System.Text;
namespace SingleInher
{
//一般动物类
class Animal
{
protected string _name; //动物名称
protected string _type; //动物所属门类
//接收名称和门类
public void AcceptDetails()
{
Console.WriteLine("请输入动物名称:");
this._name = Console.ReadLine();
Console.WriteLine("请输入所属门类:");
this._type = Console.ReadLine();
}
//显示动物的名称和所属门类
public void DisplayDetails()
{
Console.WriteLine();
Console.WriteLine("{0} 动物的详细信息如下:", this._name);
Console.WriteLine("姓名:{0}", this._name);
Console.WriteLine("门类:{0}", this._type);
}
}
//哺乳动物类,继承一般动物类
class Mammal : Animal
{
private double _temperature; //恒定温度值
private string _isViviparous; //是否为胎生
//接收哺乳动物的特征信息
public void AcceptCharacter()
{
Console.WriteLine("请输入哺乳动物的恒定温度:");
try
{
_temperature = Convert.ToDouble(Console.ReadLine());
}
catch //捕捉异常,例如:若输入字母,在转换成Decimal时会引发异常
{
Console.WriteLine("请为温度输入数字!");
}
Console.WriteLine("该哺乳动物是否胎生:");
_isViviparous = Console.ReadLine();
}
//显示哺乳动物的特征信息
public void DisplayCharacter()
{
Console.WriteLine();
Console.WriteLine("{0} 的特征包括:", this._name);
Console.WriteLine("恒定温度:{0}", _temperature.ToString());
Console.WriteLine("是否胎生:{0}", _isViviparous);
}
}
class Program
{
static void Main(string[] args)
{
Mammal objM = new Mammal();
objM.AcceptDetails();//访问父类方法
objM.AcceptCharacter();
objM.DisplayDetails();//访问父类方法
objM.DisplayCharacter();
}
}
}
2.6 输出结果
3、使用抽象类和抽象方法计算不同形状的面积
3.1 问题
编写一个程序,使用抽象类和抽象方法来计算不同形状的面积。
3.2 说明
先定义一个抽象类,该抽象类有一名为计算面积的方法为抽象方法。
3.3 解题思路
定义好抽象类和抽象方法后,具体的类继承该抽象类,实现抽象方法。该问题中定义两个具体形状类:矩形和圆。
3.4 解决方案:
(1)创建一个基于控制台的项目“AbstractClass”。
(2)建立一个抽象类,命名为“Figure”, 定义变量和抽象方法Area()。
(3)定义两个具体类Square和Circle继承自Figure类,在各自类中分别实现Area()方法。
(4)创建一个测试类,分别调用Square和Circle类。
(5)添加以下完整的代码
3.5 程序
using System;
abstract class Figure//抽象类定义,表示一个抽象图形
{
protected double x = 0, y = 0;
public Figure(double a, double b)
{ x = a; y = b; }
public abstract void Area(); //抽象方法,无实现代码,抽象图形无法计算面积
}
class Square : Figure //类Square定义,矩形类
{
public Square(double a, double b)
: base(a, b)
{ }
public override void Area() //不能使用new,必须用override
{ Console.WriteLine("矩形面积是:{0}", x * y); } //显示矩形面积
}
class Circle : Figure //类Circle定义,圆类
{
public Circle(double a)
: base(a, a)
{ }
public override void Area()
{ Console.WriteLine("圆面积是:{0}", 3.14 * x * y); } //显示圆的面积
}
class Class1
{
static void Main(string[] args)
{
Square s = new Square(20, 30);
Circle c = new Circle(10);
s.Area();
c.Area();
}
}
3.6 程序输出结果
4、使用委托和事件实现模拟闹铃
4.1 问题
编写一个模拟闹铃的程序,具有闹铃,继续闹铃,打会盹儿,停止闹铃的功能。
4.2 说明
编写一个程序用来模拟闹钟闹起床流程,有一个闹钟类AlarmClock,催促起床类WakeMeUp,使用类AlarmDriver。调用类引发开始闹铃事件,闹钟类调用委托方法进行处理,催促起床类和用户进行交互。
4.3 解题思路
在此问题中,可以设计考试类来控制流程。闹钟类应该有一个可以引发开始闹铃的方法由使用类调用。这是一种常用的类设计方式,就是建立一些实体类,这些实体类由控制流程的类来进行关联。
4.4 解决方案
(1)创建一个基于控制台的项目“EventSample”。
(2)建立一个类,命名为“AlarmEventArgs”, 定义事件参数类。
(3)声明自定义代理类型,它的事件参数的类型为AlarmEventArgs。
(4)创建包含事件和触发事件方法的类:AlarmClock。
(5)创建包含事件处理方法的类:WakeMeUp。
(6)创建包含程序入口方法的类:AlarmDriver。
(7)添加以下完整的代码
4.5 代码
using System;
using System.Collections.Generic;
using System.Text;
namespace EventSample
{
//
// 定义事件参数类
//
public class AlarmEventArgs : EventArgs
{
private readonly bool snoozePressed;
private readonly int nrings;
//构造函数
public AlarmEventArgs(bool snoozePressed, int nrings)
{
this.snoozePressed = snoozePressed;
this.nrings = nrings;
}
// 定义属性,私有字段nrings
public int NumRings
{
get { return nrings; }
}
// 定义属性,返回私有字段snoozePressed
public bool SnoozePressed
{
get { return snoozePressed; }
}
// 下面的属性返回一个字符串
public string AlarmText
{
get
{
if (snoozePressed)
{
return ("别打打盹儿,快起床!");
}
else
{
return ("起床!");
}
}
}
}
// 声明自定义代理类型,它的事件参数的类型为AlarmEventArgs
public delegate void AlarmEventHandler(object sender, AlarmEventArgs e);
// 包含事件和触发事件方法的类
public class AlarmClock
{
private bool snoozePressed = false;
private int nrings = 0;
private bool stop = false;
// 定义两个属性
public bool Stop
{
get { return stop; }
set { stop = value; }
}
public bool SnoozePressed
{
get { return snoozePressed; }
set { snoozePressed = value; }
}
// 声明事件,它所使用的代理类型为AlarmEventHandler
public event AlarmEventHandler Alarm;
// 触发事件的方法
protected virtual void OnAlarm(AlarmEventArgs e)
{
if (Alarm != null)
{
// 通过代理调用事件处理方法
Alarm(this, e);
}
}
// 在该方法中循环调用OnAlarm方法来触发事件,直到
// stop字段的值为true
public void Start()
{
for (; ; )
{
nrings++;
if (stop)
{
break;
}
else if (snoozePressed)
{
// 程序暂停1秒钟
System.Threading.Thread.Sleep(1000);
{ // 创建事件参数对象实例
AlarmEventArgs e = new AlarmEventArgs(snoozePressed,
nrings);
OnAlarm(e); // 触发事件,在事件处理方法中会
// 相应地设置snoozePressed和stop
// 字段的值(通过属性来设置)
}
}
else
{
// 程序暂停0.3秒
System.Threading.Thread.Sleep(300);
AlarmEventArgs e = new AlarmEventArgs(snoozePressed,
nrings);
OnAlarm(e); // 触发事件
}
}
}
}
// 包含事件处理方法的类
public class WakeMeUp
{
// 事件处理方法
// 在该方法中根据用户的输入来通过事件参数sender
// 设置事件发送者属性,
public void AlarmRang(object sender, AlarmEventArgs e)
{
Console.WriteLine(e.AlarmText + "\n");
if (!(e.SnoozePressed))
{
if (e.NumRings % 10 == 0)
{
Console.WriteLine("继续闹铃?输入Y");
Console.WriteLine("打会盹儿?输入N");
Console.WriteLine("停止闹铃?输入Q");
String input = Console.ReadLine();
if (input.Equals("Y") || input.Equals("y")) return;
else if (input.Equals("N") || input.Equals("N"))
{
// 通过事件参数sender来访问触发事件的对象
((AlarmClock)sender).SnoozePressed = true;
return;
}
else
{
((AlarmClock)sender).Stop = true;
return;
}
}
}
else
{
Console.WriteLine(" 继续闹铃?输入Y");
Console.WriteLine(" 停止闹铃?输入Q");
String input = Console.ReadLine();
if (input.Equals("Y") || input.Equals("y")) return;
else
{
((AlarmClock)sender).Stop = true;
return;
}
}
}
}
// 包含程序入口方法的类
public class AlarmDriver
{
// 入口方法
public static void Main(string[] args)
{
// 创建事件接收者对象实例
WakeMeUp w = new WakeMeUp();
// 创建事件发送者对象实例
AlarmClock clock = new AlarmClock();
// 把事件处理方法绑定到发送者的事件上
clock.Alarm += new AlarmEventHandler(w.AlarmRang);
clock.Start();
}
}
}
4.6 输出结果
5、多态(polymorphism)
C#可以通过使用关键字virtual在基类中定义虚方法,用override在派生类重载虚方法实现多态。以飞机类和客机类的代码来说明如何实现多态。
5.1 解题思路
分别创建类Plane和Airliner的实例p和a,先调用类Plane的方法Report,然后把p指向a,再调用Report方法,此时实际上调用了类Airliner被重载的方法Report。
5.2 代码
using System;
using System.Collections.Generic;
using System.Text;
namespace Polymorphism
{
class Plane //定义飞机类
{
public int wheels; //飞机轮子数量
protected int weight; //飞机重量,是受保护的变量
public Plane()
{
;
}
public Plane(int w1, int w2)
{
wheels = w1;
weight = w2;
}
//定义虚方法
public virtual void Report()
{
Console.WriteLine("The plane's wheel number is {0},weight is
{1}",wheels,weight);
}
}
class Airliner : Plane //定义客机类,从飞机类中继承
{
int passengers; //客机乘客数量
public Airliner(int w1,int w2,int p):base(w1,w2)
{
passengers = p;
}
//重载父类中的方法
public override void Report()
{
Console.WriteLine("The Airliner's wheel number is {0},weight is {1},passengers
is {2}", wheels, weight, passengers);
}
}
class Program
{
static void Main(string[] args)
{
Plane p = new Plane();
Airliner a = new Airliner(2, 300, 100);
p.Report();
p = a;
p.Report();
}
}
}
6、使用Hashtable对象存储数据项
6.1 问题
编写一个电脑信息类:包括电脑的型号、价格和出厂日期。程序应根据用户输入的型号来显示相应的电脑信息。
要求使用Hashtable对象存储电脑信息,并且按照型号排列输出,可以输入一个型号,程序检查判断集合中是否存在该型号的电脑,如果存在则显示具体信息。
6.2 解题思路
可以创建一个包含电脑型号、价格和出厂日期的类Computer,电脑的详细信息再作为一个元素存在Hashtable集合中
//创建Hashtable对象
Hashtable objComputers = new Hashtable();
//添加值
objComputers.Add("lenovo2008", new Computer("lenovo2008",5999.9f, new DateTime(2007, 12, 3)));
objComputers.Add("HP1000", new Computer("HP1000", 7020, new DateTime(2008, 1, 13)));
objComputers.Add("Dell600", new Computer("Dell600", 5240, new DateTime(2007, 10, 23)));
objComputers.Add("Benq1800", new Computer("Benq1800", 5620, new DateTime(2008, 4, 30)));
objComputers.Add("Sony7120", new Computer("Sony7120", 8620, new DateTime(2008, 2, 2)));
objComputers.Add("Samsung076", new Computer("Samsung076", 7623, new DateTime(2008, 1, 10)));
排列输出可以通过下面这种方式输出:
ArrayList akeys = new ArrayList(objComputers.Keys);
akeys.Sort();
foreach (string key in akeys){
Computer objComputer = (Computer)objComputers[key];
Console.WriteLine("型号:"+objComputer.Model + ";价格:" +objComputer.Price.ToString() + "¥;" + "出厂日期:" + objComputer.CDate.ToLongDateString());
}
6.3 推荐步骤
(1)创建一个基于控制台的C#应用程序,命名为“HashtableExm”。
(2)创建Computer类。
(3)完整的代码如下:
6.4 代码
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
namespace HashtableExm
{
//创建电脑类
public class Computer
{
private string _cModel;
private float _price;
private DateTime _cDate;
//获取电脑的型号
public string Model
{
get
{
return _cModel;
}
}
//获取电脑的价格
public float Price
{
get
{
return _price;
}
}
//获取电脑的出厂日期
public DateTime CDate
{
get
{
return _cDate;
}
}
//构造函数
public Computer(string cModel,float cPrice,DateTime cDate)
{
this._cModel = cModel;
this._price = cPrice;
this._cDate = CDate;
}
}
class Program
{
static void Main(string[] args)
{
//创建Hashtable对象
Hashtable objComputers = new Hashtable();
//添加值
objComputers.Add("lenovo2008", new Computer("lenovo2008",5999.9f, new DateTime (2007, 12, 3)));
objComputers.Add("HP1000", new Computer("HP1000", 7020, new DateTime(2008, 1, 13)));
objComputers.Add("Dell600", new Computer("Dell600", 5240, new DateTime(2007, 10, 23)));
objComputers.Add("Benq1800", new Computer("Benq1800", 5620, new DateTime(2008, 4,30)));
objComputers.Add("Sony7120", new Computer("Sony7120", 8620, new DateTime(2008, 2, 2)));
objComputers.Add("Samsung076", new Computer("Samsung076", 7623, new DateTime(2008, 1, 10)));
ArrayList akeys = new ArrayList(objComputers.Keys);
akeys.Sort();
foreach (string key in akeys)
{
Computer objComputer = (Computer)objComputers[key];
Console.WriteLine("型号:"+objComputer.Model + ";价格:" +objComputer.Price.ToString() + "¥;" + "出厂日期:" +objComputer.CDate.ToLongDateString());
}
Console.Write("输入电脑的型号:");
string model = Console.ReadLine();
//检查是否存在该值
if (objComputers.ContainsKey(model))
{
//搜索型号并从列表中获取对象
Computer objComputer = (Computer)objComputers[model];
Console.WriteLine("该型号的价格:" + objComputer.Price.ToString() + "¥;" + "出厂日期:" + objComputer.CDate.ToLongDateString());
}
else
{
Console.WriteLine("该型号未找到!");
}
Console.ReadLine();
}
}
}
6.5 运行效果图
7.练习
7.1 练习一
7.1.1 问题
设计项目s4-1。创建一个Vehicle类并将它声明为抽象类。在Vehicle类中声明一个NoOfWheels方法 使它返回一个字符串值。创建两个类Car和Motorbike从Vehicle类继承 并在这两个类中实现NoOfWheels方法。在Car类中 应当显示“四轮车”信息 而在Motorbike类中应当显示“双轮车”信息。创建另一个带Main方法的测试类,在该类中创建Car和Motorbike的实例 并在控制台中显示消息。
7.2 练习二
7.2.1 问题
设计项目s4-2。定义一个接口,用于描述动物的生活习性,包含食性、捕食、分布区域、睡觉方式、群居方式等;接口里面包括属性和方法。
例如:
interface IHabit
{
void CatchFood();
string food{get;set;}
……
}
然后定义猫、老虎、狮子、大象等几个动物类来实现该接口;
最后定义一个调用类,来运行程序,查看接口的使用情况。
7.3 练习三
7.3.1 问题
设计项目s4-3。抽象类、继承、接口综合:设计一个系统 XXX门的实现过程。流程:设计一张抽象的门Door,那么对于这张门来说就应该拥有所有门的共性:开门openDoor()和关门closeDoor() ,然后对门进行另外的功能设计,防盗–theftproof()、防水–waterproof()、防弹–bulletproof()、防火、防锈…… 要求:利用继承、抽象类、接口的知识设计该门。