C#入门学习——面向对象多态

当我们想让一个对象,能够表现出多种状态(类型)的时候,就要用到多态。

实现多态有三种手段,下面我来分别介绍。

虚方法

1.首先,使用关键字virtual标记父类中的方法,

        public virtual void SayHello()
        {
            Console.WriteLine("我是人类");
        }

表示这个方法可以被子类重写一遍。

子类中如果想要重写父类的方法,用override关键字标记,

        public override void SayHello()
        {
            Console.WriteLine("我是中国人,我叫{0}", this.Name);
        }

2.然后创建子类对象

            Chinese cn1 = new Chinese("韩梅梅");
            Chinese cn2 = new Chinese("李雷");
            Japanese j1 = new Japanese("树下君");
            Japanese j2 = new Japanese("井边子");
            Korea k1 = new Korea("金秀贤");
            Korea k2 = new Korea("金贤秀");
            American a1 = new American("科比布莱恩特");
            American a2 = new American("奥尼尔");

子类:(每一个子类都类似,这里就不重复表示出来了)

    public class Chinese : Person
    {
        public Chinese(string name) : base(name)
        {

        }
        public override void SayHello()
        {
            Console.WriteLine("我是中国人,我叫{0}", this.Name);
        }
    }

3.创建一个父类的数组,用来存储子类对象

Person[] pers = { cn1, cn2, j1, j2, k1, k2, a1, a2 };

4.最后输出

            for (int i = 0; i < pers.Length; i++)
            {
                //    if(pers[i]is Chinese)
                //    {
                //        ((Chinese)pers[i]).SayHello();
                //    }
                //    else if(pers[i] is Japanese)
                //    {
                //        ((Japanese)pers[i]).SayHello();
                //    }
                //    else if(pers[i] is Korea)
                //    {
                //        ((Korea)pers[i]).SayHello();
                //    }
                //    else
                //    {
                //        ((American)pers[i]).SayHello();
                //    }
                pers[i].SayHello();
            }

可以看出,如果没有使用多态,就需要写很多的if elseif判断是哪一个子类在调用方法,这样对于我们来说实在是太麻烦了。当我们用了多态之后,只需要这一行代码就能轻松的表示出所有类型。如果有新的类加入,只需要再创建对象放进数组就可以了。

练习

经理十一点打卡,员工九点打卡,程序员不打卡,使用多态

    public class Employee
    {
        public  virtual void DaKa()
        {
            Console.WriteLine("九点打卡");
        }
    }
    public class Manger : Employee
    {
        public  override void DaKa()
        {
            Console.WriteLine("经理十一点打卡");
        }
    }
    public class Programmer : Employee
    {
        public override void DaKa()
        {
            Console.WriteLine("程序员不打卡");
        }
    }
            Employee em = new Employee();
            Manger ma = new Manger();
            Programmer pr = new Programmer();
            Employee[] emp = { em, ma, pr };
            for (int i = 0; i < emp.Length; i++)
            {
                emp[i].DaKa();
            }
            Console.ReadKey();

抽象类

来看这样一个问题:狗狗会叫,猫咪也会叫,使用多态。

思路:创建一个父类Animal,让子类重写父类“叫”的方法。

但是这会出现一个问题,我们知道狗的叫声,猫的叫声,但是父类Animal怎么叫?我们并不知道是什么动物在叫。

    public class Animal
    {
        public void Bark()
        {
            //?怎么叫
        }
    }

所以这个方法没法写出来。

那么这个时候如果想实现多态,我们可以用抽象类来解决。

我们使用abstract关键字来标记一个类是抽象类,同样用abstruct关键字来标记一个方法是抽象方法,但是这个抽象方法没有方法体。

    public abstract class Animal
    {
        public abstract void Bark();
    }

特点

1.抽象成员必须标记为abstract,并且不能有任何实现。

2.抽象成员必须在抽象类中

 

3.抽象类不能被实例化(因为这个类如果实例化,并不能调用到自己的任何成员,没有意义。)

 4.子类继承抽象类后,必须把父类中的所有抽象成员都重写(除非子类也是一个抽象类,可以不重写)

 5.抽象成员的访问修饰符不能是private(如果是private,子类就不能访问到该方法,也就不能实现)

6.在抽象类中可以包含实例成员,并且抽象类的实例成员可以不被子类实现(虽然自己用不了但是子类都可以继承)

7.抽象类是有构造函数的,虽然不能被实例化。

8.如果父类的抽象方法中有参数,那么继承这个抽象父类的子类在重写父类的方法的时候,必须传入相对应的参数。如果抽象父类的抽象方法中有返回值,那么子类在重写这个抽象方法的时候也必须有返回值。

    public abstract class Animal
    {
        public abstract void Bark();
    }
    public class Dog : Animal
    {
        public override void Bark()
        {
            Console.WriteLine("狗狗汪汪的叫");
        }
    }
    public class Cat : Animal
    {
        public override void Bark()
        {
            Console.WriteLine("猫咪喵喵的叫");
        }
    }
            Animal a = new Dog();
            a.Bark();
            Console.ReadKey();

现在如果有更改,只需要更改new Dog()这一部分的代码就可以了。

再一次大大减少了代码的差异。

练习

使用多态求矩形的面积和周长以及圆形的面积和周长

思路:抽象出一个形状类,使用多态,让子类圆形和矩形分别重写面积周长方法

public abstruct class Shape
{
    public abstruct double GetArea();
    public abstract double GetPerimeter();
}
public class Circle : Shape
{
    private double _r;
    public doubule R
    {
        get { return _r; }
        set { _r = value; }
    }
    public Circle(double r)
    {
        this.R = r;
    }
    public override double GetArea(){
    {
        return Math.PI * this.R * this.R;
    }
    public override double GetPerimeter()
    {
        return Math.PI * this.R * 2;
    }
}
public class Square : Shape
{
    private double _height;
    public double Herght
    {
        get { return _height; }
        set { _height = value; }
    }
    private double _width;
    public double Width
    {
        get { return _width; }
        set { _width = value; }
    }
    public Square(double height,double width)
    {
        this.Height = height;
        this.Width = width;
    }
    public override double GetArea()
    {
        return this.Height * this.Width;
    }
    public override double GetPerimeter()
    {
        return (this.Height * this.Width) * 2;
    }
}
Shape shape = new Square(5,6);
double area = shape.GetArea();
double perimeter = shape.GetPerimeter();
Console.WriteLine("这个形状的面积是{0},周长是{1}",area,perimeter);
Console.ReadKey();

小结

如果父类中的方法有默认的实现,并且父类需要被实例化,可以考虑将父类定义成一个普通类,用虚方法来实现多态。
如果父类中的方法没有默认实现,父类也不需要被实例化,则可以将该类定义为抽象类。


练习

用多态来实现,将移动硬盘或者U盘,或者mps插到电脑上进行读写数据

思路:

首先,我们可以把移动硬盘,MP3,U盘抽象成一个类叫做可移动存储设备。这个父类具有读写的两个方法。

但是我们不知道插在电脑上的设备具体是哪一个,所以这两个方法我们不知道该如何写。因此将父类写成一个抽象类,让子类重写父类的方法,以到达具体设备读写功能。

接着想,电脑也有两个方法,读数据,写数据。电脑怎么知道我们使用的是哪一个移动设备呢?

写一个电脑类,在电脑类中写一个返回类型为移动存储设备的字段和属性,通过传一个子类的移动设备,然后让这个传进来的子类,去进行指定的读写操作,完成多态的实现。

    public abstract class MobileStorage
    {
        public abstract void Read();
        public abstract void Wirte();
    }
    public class MobileDisk : MobileStorage
    {
        public override void Read()
        {
            Console.WriteLine("移动硬盘在读取数据");
        }
        public override void Write()
        {
            Console.WriteLine("移动硬盘在写入数据");
        }
    }
    public class MP3 : MobileStorage
    {
        public override void Read()
        {
            Console.WriteLine("MP3在读取数据");
        }
        public override void Write()
        {
            Console.WriteLine("MP3在写入数据");
        }
        public void PlayMusic()
        {
            Console.WriteLine("MP3在播放音乐");
        }
    }
    public class UDisk : MobileStorage
    {
        public override void Read()
        {
            Console.WriteLine("U盘在读取数据");
        }
        public override void Write()
        {
            Console.WriteLine("U盘在写入数据");
        }
    }
public class Computer
{
    private MobileStorage _ms;
    public MobileStorage Ms
    {
        get { return _ms; }
        set { _ms = value; }
    }
    public void CpuRead()
    {
        Ms.Read();
    }
    public void CpuWrite()
    {
        Ms.Write();
    }
}
MobileStorage ms = new UDisk();
Computer cpu = new Computer();
cpu.Ms = ms;
cpu.CpuRead();
cpu.CpuWrite();
Console.ReadKey();

 重写父类ToString

ToString是父类object的虚方法,子类可重写

    public class Person
    {
        public override string ToString()
        {
            return "hello world";
        }
    }
            Person p = new Person();
            Console.WriteLine(p.ToString());
            Console.ReadKey();

 


接口

接口就是一个规范,一个能力。

[public] interface 接口名(I..able)
{
    成员;
}

特点

1.一个类可以同时继承一个类并实现多个接口。如果一个子类同时继承了父类A,并实现了接口IA,那么语法上A必须在IA前面

2.不允许有访问修饰符 默认为public。不能有字段和构造函数。不能有任何实现。可以有方法、自动属性、索引器、事件。

3.实现接口的子类必须实现该接口的全部成员。

4.接口与接口之间可以继承,并且可以多继承。但是接口并不能继承一个类,类可以继承接口。

    public interface M1
    {
        void Test1();
    }
    public interface M2
    {
        void Test2();
    }
    public interface M3//:Person
    {
        void Test3();
    }
    public interface SupperInterface:M1,M2,M3
    {

    }
    public class Car : SupperInterface
    {
        public void Test1()
        {
            throw new NotImplementedException();
        }

        public void Test2()
        {
            throw new NotImplementedException();
        }

        public void Test3()
        {
            throw new NotImplementedException();
        }
    }

5.接口不允许实例化

显示实现接口

显示实现接口就是为了解决方法的重名问题。

            IFlyable fly = new Bird();
            fly.Fly();
            Bird bird = new Bird();
            bird.Fly();
            Console.ReadKey();
    public class Bird:IFlyable
    {
        public void Fly()
        {
            Console.WriteLine("鸟会飞");
        }
        void IFlyable.Fly()
        {
            Console.WriteLine("我是接口的飞");
        }
    }
    public interface IFlyable
    {
        void Fly();
    }

 

练习

麻雀会飞,鹦鹉会飞,鸵鸟不会飞,企鹅不会飞,直升机会飞,用多态实现

思路:飞机不能抽象成鸟类,但是和鸟类有相同的方法,可以使用接口

    public abstract class Bird
    {
        public double Wings
        {
            get;
            set;
        }
        public void EatAndDrink()
        {
            Console.WriteLine("我会吃喝");
        }
        //public abstract void Fly();
        //public void Fly()
        //{
        //    Console.WriteLine("会飞");
        //}
    }
    public interface ISpeakable
    {
        void Speak();
    }
    public interface IFlyable
    {
        void Fly();
    }
    public class MaQue:Bird,IFlyable
    {
        public void Fly()
        {
            Console.WriteLine("我是麻雀我会飞");
        }
    }
    public class YingWu : Bird,IFlyable,ISpeakable
    {
        public void Speak()
        {
            Console.WriteLine("鹦鹉会学人类说话");
        }
        public void Fly()
        {
            Console.WriteLine("我是鹦鹉我会飞");
        }
    }
    public class TuoNiao : Bird
    {

    }
    public class QiE : Bird
    {

    }
    public class Plane : IFlyable
    {
        public void Fly()
        {
            Console.WriteLine("我是直升机我会飞");
        }
    }
            IFlyable fly = new Plane();
            fly.Fly();
            Console.ReadKey();

总结

什么时候用虚方法来实现多态?

这个方法可以写,还需要创建这个父类的对象。

什么时候用抽象类来实现多态?

在提供这几个类中,如果说能抽象出一个父类,并且父类当中必须得写上这几个子类共有的一个方法,还不知道如何写这个方法。

什么时候用接口来实现多态?

这几个类中根本找不出父类,但都有一个共同的方法。


访问修饰符

public:公开的公共的
private:私有的,只能在当前类的内部访问
protected:受保护的,只能在当前类的内部以及该类的子类中访问
internal:只能在当前项目中访问,在同一个项目中,internal和public的权限是一样的
protected internal:

修饰类的只有public,internal
不手动加public,默认internal
可访问性不一致,子类的访问权限不能高于父类的访问权限,会暴露父类的成员


简单工厂设计模型

设计模型:设计这个项目的一种方式。

简单工厂设计模型:

在不知道用户需求的时候,工厂依然需要生产运作,那么工厂生产一个父类出来,是一定不会错的。

 抽象一个父类

    public abstract class NoteBook
    {
        public abstract void SayHello();
    }
    public class Lenovo : NoteBook
    {
        public override void SayHello()
        {
            Console.WriteLine("我是联想");
        }
    }
    public class Acer : NoteBook
    {
        public override void SayHello()
        {
            Console.WriteLine("我是鸿基");
        }
    }
    public class Dell : NoteBook
    {
        public override void SayHello()
        {
            Console.WriteLine("我是戴尔");
        }
    }
    public class IBM : NoteBook
    {
        public override void SayHello()
        {
            Console.WriteLine("我是ibm");
        }
    }
            Console.WriteLine("请输入您想要的笔记本品牌");
            string brand = Console.ReadLine();
            NoteBook nb = GetNoteBook(brand);
            nb.SayHello();
            Console.ReadKey();

写一个方法,根据用户需要赋值给父类

*简单工厂核心*

        public static NoteBook GetNoteBook(string brand)
        {
            NoteBook nb = null;
            switch (brand)
            {
                case "Lenovo":nb = new Lenovo();
                    break;
                case "IBM":nb = new IBM();
                    break;
                case "Dell":nb = new Dell();
                    break;
                case "Acer":nb = new Acer();
                    break;
            }
            return nb;
        }
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值