【C#】零基础学会C#四——继承&抽象类和抽象方法&使用委托和事件实现模拟闹铃&多态&对象存储数据项

291 阅读12分钟

零基础学会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()、防火、防锈…… 要求:利用继承、抽象类、接口的知识设计该门。