多态
定义:让一个对象能够表现出多种的状态(类型)
实现多态的三种手段:虚方法、抽象类、接口
虚方法(virtual):
将父类的方法标记为virtual,表示是一个虚方法。将子类的方法标记为override,表示重写父类的方法,而不是隐藏父类的方法
鸭子案例:
真实的鸭子嘎嘎叫,木头鸭子吱吱叫,橡皮鸭子叽叽叫
internal class Program
{
static void Main(string[] args)
{
RealDuck duck = new MTDuck();
duck.Bark(); //木头鸭子吱吱叫
Console.ReadKey();
}
}
class RealDuck
{
public virtual void Bark()
{
Console.WriteLine("真实的鸭子嘎嘎叫");
}
}
class MTDuck:RealDuck
{
public override void Bark()
{
Console.WriteLine("木头鸭子吱吱叫");
}
}
class XPDuck:RealDuck
{
public override void Bark()
{
Console.WriteLine("橡皮鸭子叽叽叫");
}
}
当我们的一个父类,指向了一个子类后。正常情况下父类只能调用到自己的成员,因为表现出来就是父类自己的类型。但如果在父类的方法前加了virtual关键字,并且在子类方法前加了override关键字,还有一点就是父类与子类的方法名必须一样。这样的话当我们调用父类的方法的时候,就会判断,父类在内存中指向了哪一个子类对象。指向了哪一个子类对象,就执行那个子类对象override的方法
抽象类(abstract)
定义:抽象类是一种不能被实例化的类,其目的是为了被其它类继承并实现其抽象方法。
案例:计算圆形、矩形的面积和周长
我们先定义一个父类Shape,这个问题的共同点在于都要计算图形的面积和周长。在父类中写两个方法,一个计算面积,一个计算周长。但是我们没办法在父类中写具体的代码,因为面积周长的计算方式因图形不同而异。所以我们只在父类中写方法而不写具体的方法体,但是这样是“不合法”的。这时候我们将方法定义为抽象方法,添加一个abstract关键字。而抽象方法所在的类也必须是抽象类,所以我们在父类中也加入abstract关键字。
internal class Program
{
static void Main(string[] args)
{
Shape s = new Circle(5);
double area = s.GetArea();
double zhouchang = s.GetZhouChang();
Console.WriteLine(area);
Console.WriteLine(zhouchang);
Console.ReadKey();
}
}
abstract class Shape
{
public abstract double GetArea();//面积
public abstract double GetZhouChang();//周长
}
class Circle : Shape //圆形
{
public double R { get; set; }
public Circle(double r)
{
this.R = r;
}
public override double GetArea()
{
return Math.PI * this.R * this.R;
}
public override double GetZhouChang()
{
return 2 * Math.PI * this.R;
}
}
class Rectangle : Shape //矩形
{
public double Height { get; set; }
public double Width { get; set; }
public Rectangle(double height, double width)
{
this.Height = height;
this.Width = width;
}
public override double GetArea()
{
return this.Height * this.Width;
}
public override double GetZhouChang()
{
return 2 * (this.Height + this.Width);
}
}
抽象类总结:
1、抽象成员必须被标记为abstract
2、抽象成员必须在抽象类中
3、抽象类不能被实例化
4、子类继承抽象类后,必须把抽象类的所有抽象成员重写(除非子类也是一个抽象类)
5、抽象成员的访问修饰符不能是private,并且抽象类的实例成员可以不被子类重写
6、抽象类是有构造函数的,虽然不能被实例化
7、如果抽象类的抽象方法有参数,那么子类在重写的时候必须也传入相应的参数(返回值)
8、如果父类中的方法有默认实现,并且父类需要被实例化,这时可以考虑将父类定义成普通类,用虚方法来实现
9、如果父类的方法没有默认实现,并且可以不被实例化,则可以将父类定义成抽象方法
单例设计模式
设计模式:为了解决某一类固定的问题
单例设计模式:保证一个类,只有一个实例
//懒汉式单例设计模式(用的时候才创建对象)
internal class Program
{
static void Main(string[] args)
{
Person.GetSinglePerson();
}
}
class Person
{
//1、构造函数私有化
private Person() { }
//2、声明一个静态的字段,来存储唯一的对象
public static Person _singlePerson = null;
//3、提供一个静态方法,用来返回全局唯一的对象
public static Person GetSinglePerson()
{
if (_singlePerson == null)
{
_singlePerson = new Person();
}
return _singlePerson;
}
}
懒汉式单例设计模式在多线程时会有安全隐患
//饿汉式设计模式
internal class Program
{
static void Main(string[] args)
{
Person.GetSinglePerson();
}
}
class Person
{
private Person() { }
//程序运行之前,这个对象就已经创建了,并且整个程序运行期间这个对象只创建一次
private static Person _singlePerson = new Person();
public static Person GetSinglePerson()
{
return _singlePerson;
}
}
简单工程设计模式
定义:让我们的程序,根据用户的需求,返回特定的对象
选电脑案例,根据品牌选择电脑
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("请输入你想要的笔记本");
string brand = Console.ReadLine();
Computer computer = ComputerFactory(brand);
if (computer == null)
{
Console.WriteLine("没有你要的品牌");
}
else
{
computer.SayHello();
}
Console.ReadKey();
}
static Computer ComputerFactory(string input)//电脑工厂
{
Computer computer = null;
switch (input)
{
case "联想":
computer = new Lenovo("联想");
break;
case "戴尔":
computer = new Dell("戴尔");
break;
case "宏碁":
computer = new Acer("宏碁");
break;
case "惠普":
computer = new HP("惠普");
break;
}
return computer;
}
}
abstract class Computer
{
public string Brand { get; set; }
public Computer(string brand)
{
this.Brand = brand;
}
public abstract void SayHello();
}
class Lenovo : Computer
{
public Lenovo(string brand) : base(brand)
{
}
public override void SayHello()
{
Console.WriteLine("我是联想笔记本");
}
}
class Dell : Computer
{
public Dell(string brand) : base(brand)
{
}
public override void SayHello()
{
Console.WriteLine("我是戴尔笔记本");
}
}
class Acer : Computer
{
public Acer(string brand) : base(brand)
{
}
public override void SayHello()
{
Console.WriteLine("我是宏碁笔记本");
}
}
class HP : Computer
{
public HP(string brand) : base(brand)
{
}
public override void SayHello()
{
Console.WriteLine("我是惠普笔记本");
}
}
打折案例,用户输入打折方式,根据选择不同计算方法不同
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("请选择打折方式 1--500减50 2--300减40 3--八折 4--九折 5--不打折");
string str = Console.ReadLine();
ZheKou z = GetZheKou(str);
if (z == null)
{
Console.WriteLine("没有你选择的打折方式");
}
else
{
double b = z.ChooseZheKou(2000);
Console.WriteLine(b);
}
Console.ReadKey();
}
static ZheKou GetZheKou(string i)
{
ZheKou z = null;
switch (i)
{
case "1":
z = new MaiMSongN(500, 50);
break;
case "2":
z = new MaiMSongN(300, 40);
break;
case "3":
z = new DaNZhe(8);
break;
case "4":
z = new DaNZhe(9);
break;
case "5":
z = new YuanJia();
break;
}
return z;
}
}
abstract class ZheKou
{
public abstract double ChooseZheKou(double i);
}
class MaiMSongN : ZheKou
{
public MaiMSongN(double m, double n)
{
this.M = m;
this.N = n;
}
public double M { get; set; }
public double N { get; set; }
public override double ChooseZheKou(double i)
{
if (i > this.M)
{
return i - (i / this.M) * this.N;
}
else
{
return i;
}
}
}
class DaNZhe : ZheKou
{
public DaNZhe(double n)
{
this.N = n;
}
public double N { get; set; }
public override double ChooseZheKou(double i)
{
return i * this.N * 0.1;
}
}
class YuanJia : ZheKou
{
public override double ChooseZheKou(double i)
{
return i;
}
}
依赖注入
定义:让一个类与另一个类产生联系
依赖注入三种方式:1、通过属性注入
2、通过构造函数注入
3、通过方法的参数注入
案例:模拟 把移动硬盘等插到电脑上读写数据
//1、通过属性注入
internal class Program
{
static void Main(string[] args)
{
//模拟把移动硬盘/U盘/MP3插到电脑上,读写数据
Computer cpu = new Computer();
cpu.MF = new MobileDisk();
cpu.Read();
cpu.Write();
Console.ReadKey();
}
}
class Computer
{
//调用插到掉闹上的移动设备的读数据的方法和写数据的方法
//1、属性注入
public MobileFather MF { get; set; }
//2、提供一个读数据的方法
public void Read()
{
this.MF.Read();
}
//3、提供一个写数据的方法
public void Write()
{
this.MF.Write();
}
}
abstract class MobileFather
{
public abstract void Read();
public abstract void Write();
}
class UDisk : MobileFather
{
public override void Read()
{
Console.WriteLine("优盘读取数据");
}
public override void Write()
{
Console.WriteLine("优盘写入数据");
}
}
class MobileDisk : MobileFather
{
public override void Read()
{
Console.WriteLine("移动硬盘读取数据");
}
public override void Write()
{
Console.WriteLine("移动硬盘写入数据");
}
}
class MP3 : MobileFather
{
public override void Read()
{
Console.WriteLine("MP3盘读取数据");
}
public override void Write()
{
Console.WriteLine("MP3写入数据");
}
public void Music()
{
Console.WriteLine("MP3听音乐");
}
}
//2、通过构造函数注入
internal class Program
{
static void Main(string[] args)
{
//模拟把移动硬盘/U盘/MP3插到电脑上,读写数据
Computer cpu = new Computer(new MobileDisk());
cpu.Read();
cpu.Write();
Console.ReadKey();
}
}
class Computer
{
public MobileFather MF { get; set; }
//2、提供一个读数据的方法
public void Read()
{
this.MF.Read();
}
//3、提供一个写数据的方法
public void Write()
{
this.MF.Write();
}
public Computer(MobileFather mF)
{
this.MF = mF;
}
}
abstract class MobileFather
{
public abstract void Read();
public abstract void Write();
}
class UDisk : MobileFather
{
public override void Read()
{
Console.WriteLine("优盘读取数据");
}
public override void Write()
{
Console.WriteLine("优盘写入数据");
}
}
class MobileDisk : MobileFather
{
public override void Read()
{
Console.WriteLine("移动硬盘读取数据");
}
public override void Write()
{
Console.WriteLine("移动硬盘写入数据");
}
}
class MP3 : MobileFather
{
public override void Read()
{
Console.WriteLine("MP3盘读取数据");
}
public override void Write()
{
Console.WriteLine("MP3写入数据");
}
public void Music()
{
Console.WriteLine("MP3听音乐");
}
}
//3、通过方法注入
internal class Program
{
static void Main(string[] args)
{
//模拟把移动硬盘/U盘/MP3插到电脑上,读写数据
Computer cpu = new Computer();
MobileDisk md = new MobileDisk();
cpu.Read(md);
cpu.Write(md);
Console.ReadKey();
}
}
class Computer
{
public void Read(MobileFather mf)
{
mf.Read();
}
public void Write(MobileFather mf)
{
mf.Write();
}
}
abstract class MobileFather
{
public abstract void Read();
public abstract void Write();
}
class UDisk : MobileFather
{
public override void Read()
{
Console.WriteLine("优盘读取数据");
}
public override void Write()
{
Console.WriteLine("优盘写入数据");
}
}
class MobileDisk : MobileFather
{
public override void Read()
{
Console.WriteLine("移动硬盘读取数据");
}
public override void Write()
{
Console.WriteLine("移动硬盘写入数据");
}
}
class MP3 : MobileFather
{
public override void Read()
{
Console.WriteLine("MP3盘读取数据");
}
public override void Write()
{
Console.WriteLine("MP3写入数据");
}
public void Music()
{
Console.WriteLine("MP3听音乐");
}
}
依赖注入案例:模拟电脑店卖电脑
以下是最终要实现的结果图
先写一个电脑类(Computer)用于存储所有电脑的父类
internal class Computer
{
public string Brand { get; set; }
public double Price { get; set; }
public string ID { get; set; }
public Computer(string brand, double price, string iD)
{
this.Brand = brand;
this.Price = price;
this.ID = iD;
}
}
然后我们顺便将四个不同品牌电脑的类写了
Lenovo.cs
internal class Lenovo : Computer
{
public Lenovo(string brand, double price, string iD) : base(brand, price, iD)
{
}
}
HP.cs
internal class HP : Computer
{
public HP(string brand, double price, string iD) : base(brand, price, iD)
{
}
}
Dell.cs
internal class Dell : Computer
{
public Dell(string brand, double price, string iD) : base(brand, price, iD)
{
}
}
Acer.cs
internal class Acer : Computer
{
public Acer(string brand, double price, string iD) : base(brand, price, iD)
{
}
}
而后写仓库类用来存储电脑的品牌与数量
Cangku.cs
internal class Cangku
{
//定义一个集合来存储所有的电脑
//List<Computer> list = new List<Computer>();//如果说这样存储所有电脑的话那么分不清楚电脑品牌,所有电脑都混在一起了
List<List<Computer>> list = new List<List<Computer>>();//这样存储将仓库根据不同电脑品牌分出不同的货架
//比如list[0]是联想货架,list[1]是戴尔货架,list[2]是宏碁货架,list[3]是惠普货架
public Cangku()
{
//在构造函数里,给仓库添加货架。
//为什么要在构造函数里添加?因为我们要确保,仓库类一创建的时候,库里面就有东西
list.Add(new List<Computer>());
list.Add(new List<Computer>());
list.Add(new List<Computer>());
list.Add(new List<Computer>());
}
/// <summary>
/// 展示仓库里的电脑信息
/// </summary>
public void ShowComputer()
{
foreach (var item in list)
{
Console.WriteLine("商品名称:{0},商品单价:{1},商品数量:{2}", item[0].Brand, item[0].Price, item.Count);
}
}
/// <summary>
/// 往仓库进货
/// </summary>
/// <param name="brand">进货的品牌名</param>
/// <param name="count">进货的数量</param>
public void JinHuo(string brand, int count)
{
for (int i = 0; i < count; i++)
{
switch (brand)
{
case "Lenovo":
list[0].Add(new Lenovo("Lenovo", 1000, Guid.NewGuid().ToString()));//往第一个货架加入1000台联想电脑,Guid.NewGuid()是随机生成id的函数
break;
case "Dell":
list[1].Add(new Dell("戴尔", 2000, Guid.NewGuid().ToString()));
break;
case "Acer":
list[2].Add(new Acer("鸿基", 3000, Guid.NewGuid().ToString()));
break;
case "HP":
list[3].Add(new HP("惠普", 4000, Guid.NewGuid().ToString()));
break;
}
}
}
/// <summary>
/// 从仓库取货
/// </summary>
/// <param name="brand">取货的品牌</param>
/// <param name="count">取货的数量</param>
/// <returns>返回类型是computer类型的数组</returns>
public Computer[] QuHuo(string brand, int count)
{
Computer[] cpus = new Computer[count];
for (int i = 0; i < cpus.Length; i++)
{
switch (brand)
{
case "Lenovo":
cpus[i] = list[0][0];//取的是第一个货架上的第一个电脑,将取得的电脑给cpus的第一个元素。取完第一个之后,剩下的本来处于第二个位置的电脑就变成第一个了,所以一直取索引为“0”的
list[0].RemoveAt(0);
break;
case "Dell":
cpus[i] = list[1][0];
list[1].RemoveAt(0);
break;
case "Acer":
cpus[i] = list[2][0];
list[2].RemoveAt(0);
break;
case "HP":
cpus[i] = list[3][0];
list[3].RemoveAt(0);
break;
}
}
return cpus;
}
}
然后写打折的类,之前我们写过,一个打折的父类和三个打折的子类
DiscountFather.cs:
internal abstract class DiscountFather
{
public abstract double GetMoney(double money);
}
MNDiscount.cs:
internal class MNDiscount : DiscountFather
{
public double M { get; set; }
public double N { get; set; }
public override double GetMoney(double money)
{
if (money > this.M)
{
return money - (int)(money / this.M) * this.N;
}
else
{
return money;
}
}
}
NDiscount.cs:
internal class NDiscount:DiscountFather
{
public double N { get; set; }
public override double GetMoney(double money)
{
return this.N * 0.1 * money;
}
}
YuanJia.cs:
internal class YuanJia : DiscountFather
{
public override double GetMoney(double money)
{
return money;
}
}
然后开始写SuperMarket类用于跟用户交互
internal class SuperMarket
{
//初始化仓库
Cangku ck = new Cangku();
public SuperMarket()
{
//给每个货架添加数据
ck.JinHuo("Lenovo", 100);
ck.JinHuo("Dell", 100);
ck.JinHuo("Acer", 100);
ck.JinHuo("HP", 100);
}
public void AskBuying()
{
//先展示仓库库存
ck.ShowComputer();
Console.WriteLine("欢迎光临笔记本超市");
Console.WriteLine("请问你需要什么?");
Console.WriteLine("有Lenovo,Dell,Acer,HP");
string brand = Console.ReadLine();
Console.WriteLine("你要多少?");
int count = Convert.ToInt32(Console.ReadLine());
//取货
Computer[] cpus = ck.QuHuo(brand, count);
//算钱(原价)
double money = CpusMoney(cpus);
Console.WriteLine("选择打折方式");
Console.WriteLine("1---原价 2--打9折 3--打8折 4--买500送100 5--买300送50");
string input = Console.ReadLine();
DiscountFather discount = GetDiscount(input);
double realMoney = discount.GetMoney(money);//打折后的价钱
Console.WriteLine("您本次应付{0}元,实付{1}元", money, realMoney);
//打印小票
ShowXiaoPiao(cpus);
}
/// <summary>
/// 根据电脑的品牌返回相应的打折方式
/// </summary>
/// <param name="input">选择的代表电脑品牌的数字</param>
/// <returns>相应品牌的打折方式</returns>
static DiscountFather GetDiscount(string input)
{
DiscountFather father = null;
switch (input)
{
case "1":
father = new YuanJia();
break;
case "2":
father = new NDiscount() { N = 9 };
break;
case "3":
father = new NDiscount() { N = 8 };
break;
case "4":
father = new MNDiscount() { M = 500, N = 100 };
break;
case "5":
father = new MNDiscount() { M = 300, N = 50 };
break;
}
return father;
}
/// <summary>
/// 算钱
/// </summary>
/// <param name="cpus"></param>
/// <returns></returns>
public double CpusMoney(Computer[] cpus)
{
double money = 0;
for (int i = 0; i < cpus.Length; i++)
{
money += cpus[i].Price;
}
return money;
}
/// <summary>
/// 展示小票信息
/// </summary>
/// <param name="cpus"></param>
public void ShowXiaoPiao(Computer[] cpus)
{
Console.WriteLine("您本次一共购买了{0}台{1}电脑", cpus.Length, cpus[0].Brand);
Console.WriteLine("以下是小票信息");
Console.WriteLine("========================================================");
for (int i = 0; i < cpus.Length; i++)
{
Console.WriteLine("商品名称:{0},商品ID{1}", cpus[i].Brand, cpus[i].ID);
}
}
}
最后在Program类里调用SuperMarket里的跟用户交互的方法即可
internal class Program
{
static void Main(string[] args)
{
SuperMarket super = new SuperMarket();
super.AskBuying();
Console.ReadKey();
}
}
接口
定义:接口侧重表现的是一种能力,谁侧重这种能力,谁就去实现这个接口
internal class Program
{
static void Main(string[] args)
{
IFly fly = new MaQue();
fly.Fly();
Console.ReadKey();
}
}
class Bird //鸟
{
}
class MaQue : Bird, IFly //麻雀
{
public void Fly()
{
Console.WriteLine("麻雀会飞");
}
}
class TuoNiao : Bird //鸵鸟
{
}
class FeiJi : IFly //飞机,飞机没办法继承鸟类
{
public void Fly()
{
Console.WriteLine("飞机会飞");
}
}
interface IFly
{
void Fly();
}
1、接口是一种规范,强调一个能力
2、只要一个类继承了一个接口,就必须实现接口中的所有成员
3、接口不允许被实例化
4、接口中的成员不允许加访问修饰符,默认为public
5、接口中的方法可以有方法、属性、索引器、事件,不能有字段和构造函数
6、接口与接口之间,可以多继承
7、接口不能继承一个类,而类可以实现一个或多个接口
8、实现接口的子类,必须实现接口的全部成员
9、一个子类可以继承一个父类和多个接口,如果一个子类继承了一个父类又继承了接口,语法上要求必须先继承父类,再继承接口
10、显示实现接口,解决方法重名问题
11、接口当一个抽象类实现的时候,需要抽象类的子类取实现接口