目录
多态
1、概念:让一个对象能够表现出多种的状态(类型) 一个接口,多个功能
2、三种实现多态手段:虚方法、抽象类、接口
3、虚方法:在父类方法前面添加一个关键字:virtual,将该方法标记为虚方法,子类就可以重写该方法,
子类重写的时候,函数名与父类一致,方法前面添加关键字override。
4、 is :判断是否是该种类型 成功,返回true 失败:false
as:将该变量转换成该种类型 如果成功转换,就转了,如果失败,返回null
if (pers[i] is Chinese)
{
Chinese ch = pers[i] as Chinese;
}
翻译:如果pers[i]是Chinese类型,就将pers[i]转换成Chinese类型,并赋值给ch对象
静态多态性
- 函数重载
- 运算符重载
动态多态性
- 动态多态性是通过 抽象类 和 虚方法 实现的
实现多态的手段: 虚方法 抽象类 接口
一、虚方法:
将父类的方法标记为虚方法,使用关键字virtual.这个函数就可以被子类重新写一遍。子类重写该方法时,使用override关键字
每个类都有一个SayHello()方法。
在声明中包含virtual关键字是为了重写方法而存在的。
eg:
using System;
namespace PolymorphismApplication
{
class Shape
{
protected int width, height;
public Shape( int a=0, int b=0)
{
width = a;
height = b;
}
public virtual int area()
{
Console.WriteLine("父类的面积:");
return 0;
}
}
class Rectangle: Shape
{
public Rectangle( int a=0, int b=0): base(a, b)
{
}
public override int area ()
{
Console.WriteLine("Rectangle 类的面积:");
return (width * height);
}
}
class Triangle: Shape
{
public Triangle(int a = 0, int b = 0): base(a, b)
{
}
public override int area()
{
Console.WriteLine("Triangle 类的面积:");
return (width * height / 2);
}
}
class Caller
{
public void CallArea(Shape sh)
{
int a;
a = sh.area();
Console.WriteLine("面积: {0}", a);
}
}
class Tester
{
static void Main(string[] args)
{
Caller c = new Caller();
Rectangle r = new Rectangle(10, 7);
Triangle t = new Triangle(10, 5);
c.CallArea(r);
c.CallArea(t);
Console.ReadKey();
}
}
}
二、里氏转换
优点
1. 提高代码的重用性,子类拥有父类的方法和属性;
2. 提高代码的可扩展性,子类可形似于父类,但异于父类,保留自我的特性。
显示转换:父类对象转换为子类类型必须使用强制转换
Person p = new Teacher();
Teacher t = (Teacher)p;//转换成功
t.SayHi();
隐式转换:子类对象转换为父类类型可以直接转
Student stu = new Student();//转换成功
Person p = stu;
p.Show();
1、特点
1)、子类可以赋值给父类,如果有一个地方,需要一个父类作为参数,我们可以给一个子类
2)、如果父类中装的是子类对象,那么可以将这个父类强制转换为子类对象
2、子类对象可以调用父类中的成员,但是父类对象永远只能调用自己的成员。
3、关键字is/as转换:
is:表示类型转换,如果能够转换成功,则返回true 否则返回false
as:表示类型转换,如果能够转换则返回对象,否则返回null
三、抽象类
C# 允许您使用关键字 abstract 创建抽象类,用于提供接口的部分类的实现。当一个派生类继承自该抽象类时,实现即完成。抽象类包含抽象方法,抽象方法可被派生类实现。派生类具有更专业的功能。
- 通过在类定义前面放置关键字 sealed,可以将类声明为密封类。当一个类被声明为 sealed 时,它不能被继承。抽象类不能被声明为 sealed。
抽象---具体
抽象类关键字:abstract
抽象类的特点:
1、不能被实例化,也就是说不能创建抽象类的对象
2、抽象成员必须被标记为abstract,并且不能有任何实现(不能有大括号,不能有方法体)
public abstract void Bark();
public abstract string Name
{
get; //get与set都不能有方法体
set;
}
3、抽象成员必须在抽象类中,也就是说普通类中不能有抽象成员
4、子类继承抽象类以后,必须实现抽象类中的所有抽象成员,除非子类也是一个抽象类,可以不重写
5、抽象成员的访问修饰符不能是private
6、抽象类中可以包含实例成员
7、抽象类是有构造函数的,虽然不能被实例化。
8、如果父类的抽象方法中有参数,那么继承这个抽象父类的子类在重写父类的方法时必须传入对应的参数,
如果抽象父类的抽象方法中有返回值,那么子类在重写这个抽象方法时也必须传入返回值
9、单根性
总之,如果父类的方法中有默认的实现,并且父类需要被实例化,这时可以考虑将父类定义成一个普通类,用虚方法实现;
如果父类中的方法没有默认实现,父类也不需要被实例化,则可以将该类定义为抽象类。
练习一:使用多态求矩形的面积和周长以及圆形的面积和周长
类:
abstract class Shape
{
public abstract void area();
public abstract void perimeter();//周长
}
class Rectangle : Shape//矩形
{
private int _width;
public int Width
{
get { return _width; }
set { _width = value; }
}
private int _height;
public int Height
{
get { return _height; }
set { _height = value; }
}
public Rectangle(int a = 0, int b = 0)
{
_width = a;
_height = b;
}
public override void area()
{
Console.WriteLine("Rectangle 类的面积:{0}", _width * _height);
}
public override void perimeter()
{
Console.WriteLine("Rectangle 类的周长:{0}", (_width + _height) * 2);
}
}
class Circle : Shape
{
private int _r;
public int R
{
get { return _r; }
set { _r = value; }
}
public Circle(int r)
{
_r = r;
}
public override void area()
{
Console.WriteLine("Circle类的面积:{0}", 3.14 * R * R);
}
public override void perimeter()
{
Console.WriteLine("Circle类的周长:{0}", 2 * 3.14 * R);
}
}
主函数:
#region 练习一: 通过虚方法 area() 来计算不同形状图像的周长和面积:
Rectangle rect = new Rectangle(10, 6);
Circle cir = new Circle(5);
rect.area();
rect.perimeter();
cir.area();
cir.perimeter();
Console.ReadKey();
#endregion
练习二:用多态实现 模拟将移动硬盘或U盘或MP3插到电脑上进行读写
类:
/// <summary>
/// 抽象的父类,可移动存储
/// </summary>
public abstract class MobileStorage
{
public abstract void Read();
public abstract void Write();
}
public class MobileDisk : MobileStorage
{
public override void Read()//override可以重写抽象方法和虚方法
{
Console.WriteLine("移动硬盘在读取数据");
}
public override void Write()
{
Console.WriteLine("移动硬盘在写入数据");
}
}
public class UDisk : MobileStorage
{
public override void Read()
{
Console.WriteLine("U盘在读取数据");
}
public override void Write()
{
Console.WriteLine("U盘在写入数据");
}
}
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 Computer
{
//方法2,拿字段存传入的父类参数
private MobileStorage _ms;
public MobileStorage Ms//字段封装属性
{
get { return _ms; }
set { _ms = value; }
}
public void CpuRead()//方法1,想拿到父类,传参数进来就行,可以传子类进来
{
Ms.Read();//方法2,通过属性拿到父类,在外面不用传参数,采用赋值
}
public void CpuWrite()
{
Ms.Write();
}
}
主函数:
#region 练习二:用多态实现 模拟将移动硬盘或U盘或MP3插到电脑上进行读写
//多态 模拟 移动硬盘 插入电脑 读写
MobileDisk md = new MobileDisk();//父类抽象类不能实例化,所以实例化子类
UDisk ud = new UDisk();
Mp3 mp = new Mp3();
MobileStorage[] ms = new MobileStorage[] { md,ud,mp};
Computer cpu = new Computer();//通过属性拿到父类,在外面不能传参数,采用赋值
cpu.Ms = ms[0];//表示把插入的MP3赋值给电脑里面的Ms属性
//cpu.CpuRead(ms[0]);
//cpu.CpuWrite(ms[0]);
cpu.CpuRead();//采用属性赋值形式,不用传参数
cpu.CpuWrite();
#endregion
四、部分类 密封类
部分类:partial class
调用:实例化对象时,不同部分类的方法都可以使用。
可以将一个类用partial关键字修饰,将该类拆成多个文件,调用的时候会把他们当做一个文件来调用
密封类:sealed 不允许被继承
丁克、定义一个密封类叫XiaoJinKU,它不能有子类
五、访问修饰符
1、public:公共的
2、private:私有的,只有自己能访问
3、protected:受保护的,只能是该类内部或者它的子类能访问
4、internal:只能在当前程序集中访问,即只能在当前命名空间中访问,或者只能在当前项目中访问
对于类来说,如果前面不添加任何访问修饰符,就相当于internal访问权限
子类的访问权限不能高于父类
六、接口
internal 访问权限大小为当前项目(当前命名空间)
关键字:interface
语法:
[访问修饰符] interface
{
属性类型 属性名称;
返回值 方法名称(参数列表);
}
对于接口类,我们习惯在接口命名的时候加上字母I,比如:IUsb,IPerson
接口特点:
1、接口中不能定义字段
2、接口中的成员不能添加访问修饰符,任何都不行
3、接口中的方法只能声明,不能定义,即方法不能有方法体-大括号
4、接口是一个规范,一个能力 电脑上的USB 鼠标、键盘都是圆口,现在都是USB
现在都用USB进行统一规范 风扇、移动硬盘 MP5 投影仪
安卓手机 一头宽一头细 高端机 TypeC
5、 只要一个类继承了这个接口,就必须实现这个接口中的所有成员
6、 接口不能被实例化
7、接口中的成员不允许添加访问修饰符,默认就是public 不能修改
8、接口中的成员不能有任何实现(“光说不做”)只是定义了一组未实现的成员
9、继承接口的成员,不需要用override来修饰
10、接口中只能有方法、属性、索引器、事件,不能有“字段”和构造函数
11、接口与接口之间可以继承,并且可以多继承。继承的接口不需要实现父接口的成员
interface Icomputer:IUsb,ImobileDivice
{
void WatchMovie();
}
12、接口不能找类作为父亲,接口的父亲只能是接口
13、一个类既可以让类作为父类,又可以让接口作为父亲,可以两者兼有。如果两者兼有,类在前,接口在后
比如:如果一个类继承了父类A,同时继承了接口IA,A要写在IA的前面像:class MyClass:A,IA{ },因为类是单继承的
class Mp3 : MP2,IUsb
{}
14、显示实现接口是为了解决接口中的方法,与该类中的方法重名问题
//显示实现接口
void IFly.DrivePlane()
{
Console.WriteLine("通过飞行员接口,我学会了飞行");
}
电脑中为什么没有A盘和B盘?
初期的电脑,存储数据用的是软盘,它的盘符就是A,后来有了升级的软盘,它的盘符是B
七、简单工厂设计模式
1、设计模式:设计项目的一种方式
由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。
该模式中包含的角色及其职责
工厂(Creator)角色
简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
抽象产品(Product)角色
简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
具体产品(Concrete Product)角色
是简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。
eg:我们在医院里看病可以根据病情的严重性指定普通医生还是科室主任来主刀手术,为了使程序简单化,我们用接口实现
工厂(Creator)角色:被外界直接调用,创建所需的产品对象。
/// <summary>
/// 接待病人,进行分诊
/// </summary>
class Patient
{
/// <summary>
/// 根据病情对病人进行分诊
/// </summary>
/// <param name="bingQing"></param>
public static void FenZhen(string bingQing)
{
IDoctor doc = null; //定义父类对象,把子类对象赋值给父类 里氏转换
switch (bingQing)
{
case "轻微":
doc = new PracticeDoctor("尼古拉斯赵四", 1);
doc.SeeDoctor();
break;
case "严重":
doc = new Profession("乔殿下", 6, "主任医师");
doc.SeeDoctor();
break;
default:
doc = new PracticeDoctor("刘能",1);
doc.SeeDoctor();
break;
}
}
}
抽象产品(Product)角色:所有实例所共有的公共接口
interface IDoctor
{
/// <summary>
/// 姓名
/// </summary>
string Name { get; set; }
/// <summary>
/// 工作年限
/// </summary>
int WorkYear { get; set; }
/// <summary>
/// 看病
/// </summary>
void SeeDoctor();
}
具体产品(Concrete Product)角色:所有创建的对象都是充当这个角色的某个具体类的实例
//专家号
class Profession : IDoctor
{
private string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
private int workYear;
public int WorkYear
{
get
{
return workYear;
}
set
{
workYear = value;
}
}
private string zhiCheng;
public string ZhiCheng
{
get { return zhiCheng; }
set { zhiCheng = value; }
}
public Profession(string s, int y, string zhi)
{
this.name = s;
this.workYear = y;
this.zhiCheng = zhi;
}
public void SeeDoctor()
{
Console.WriteLine("医生:{0},工作了{1}年,他的职称是{2},是个妙手回春的老中医,擅长治疗各类不孕不育",
name,workYear,zhiCheng);
}
}
//普通号
class PracticeDoctor : IDoctor
{
private string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
private int workYear;
public int WorkYear
{
get
{
return workYear;
}
set
{
workYear = value;
}
}
/// <summary>
/// 实例化普通医生
/// </summary>
/// <param name="s">姓名</param>
/// <param name="y">工作年限</param>
public PracticeDoctor(string s, int y)
{
this.name = s;
this.workYear = y;
}
public void SeeDoctor()
{
Console.WriteLine("实习医生:{0},工作了{1}年,经验尚可,可以试试。", name, workYear);
}
}
主函数:
Console.WriteLine("请问你得了什么病?");
string bing = Console.ReadLine();
Console.WriteLine("病情如何?(严重/轻微)");
string answer = Console.ReadLine();
Patient.FenZhen(answer);
Console.Read();
练习:
工厂里可以生产联想、惠普、Dell等笔记本,用户告诉我笔记本型号,我就可以为他供货。用简单工厂模式实现。
抽象产品 接口:
interface IComputerBrand
{
/// <summary>
/// 电脑型号
/// </summary>
string brand { set; get; }
/// <summary>
/// 价格
/// </summary>
int price { set; get; }
/// <summary>
/// 打印信息
/// </summary>
void showInfo();
}
具体产品:
class Lenovo : IComputerBrand
{
private string _brand;
public string brand
{
get { return _brand; }
set { _brand = value; }
}
private int _price;
public int price
{
get { return _price; }
set { _price = value; }
}
/// <summary>
/// 实例化Lenovo对象
/// </summary>
/// <param name="n"></param>
/// <param name="p"></param>
public Lenovo(string b,int p)
{
this.brand = b;
this.price = p;
}
public void showInfo()
{
Console.WriteLine("您选择的电脑型号:{0},价格:{1}。",brand,price);
}
}
class Hp : IComputerBrand
{
private string _brand;
public string brand
{
get { return _brand; }
set { _brand = value; }
}
private int _price;
public int price
{
get { return _price; }
set { _price = value; }
}
/// <summary>
/// 实例化Lenovo对象
/// </summary>
/// <param name="n"></param>
/// <param name="p"></param>
public Hp(string b, int p)
{
this.brand = b;
this.price = p;
}
public void showInfo()
{
Console.WriteLine("您选择的电脑型号:{0},价格:{1}。", brand, price);
}
}
class Dell : IComputerBrand
{
private string _brand;
public string brand
{
get { return _brand; }
set { _brand = value; }
}
private int _price;
public int price
{
get { return _price; }
set { _price = value; }
}
/// <summary>
/// 实例化Lenovo对象
/// </summary>
/// <param name="n"></param>
/// <param name="p"></param>
public Dell(string b, int p)
{
this.brand = b;
this.price = p;
}
public void showInfo()
{
Console.WriteLine("您选择的电脑型号:{0},价格:{1}。", brand, price);
}
}
工厂:
class Computer
{
public static void Choose(string brand)
{
IComputerBrand cpt = null;
switch (brand)
{
case "Lenovo":
cpt = new Lenovo("联想", 5888);
cpt.showInfo();
break;
case "Dell":
cpt = new Dell("戴尔", 4999);
cpt.showInfo();
break;
case "Hp":
cpt = new Hp("惠普", 4666);
cpt.showInfo();
break;
default:
Console.WriteLine("输入错误");
break;
}
}
}
主函数:
Console.WriteLine("请输入您想要的品牌:(Lenovo/Dell/Hp)");
string s = Console.ReadLine();
Computer.Choose(s);
Console.ReadKey();
总结:
虚方法:父类的方法加关键字virtual,有方法体;子类添加关键字override,重写父类的虚方法,方法名要与父类一致。
抽象类:定义类的时候添加关键字abstract,抽象类中的成员只声明,不实现;子类继承抽象类的时候,要全部实现父类的抽
象成员;抽象类可以有构造函数,但是没什么卵用,因为它不能被实例化。一个子类只能继承自一个抽象类。
抽象类中可以有普通成员。
接口:定义接口的时候在前面添加interface关键字,也就是说把class关键字替换成interface关键字,它就变成了接口。
接口只声明不实现,光说不干!接口可以多继承,也就是说一个子类可以继承自多个接口,把接口当叔叔,一个子类可以
有多个叔叔。
用接口或抽象类实例化子类时,不能调用子类自己和继承自其他接口的方法