一、封装
- 封装被定义为“把一个或多个项目封闭在一个物理的或者逻辑的包中”。在面向对象程序设计方法论中,封装是为了防止对实现细节的访问;
- 抽象和封装是面向对象程序设计的相关特性。抽象允许相关信息可视化,封装则使开发者实现所需级别的抽象;
- C# 封装根据具体的需要,设置使用者的访问权限,并通过访问修饰符来实现,一个访问修饰符定义了一个类成员的范围和可见性;
- C#支持的访问修饰符:
注记:A:父类 ~ B:儿子 ~ C:妻子 ~ D:私生子(不在家)
public:所有对象都可以访问;(地球人都知道)
private:对象本身在对象内部可以访问;(隐私,只有A自己知道)
protected:只有该类对象及其子类对象可以访问;(A、B、D都知道,妻子C不知道)
internal:同一个程序集的对象可以访问;(A、B、C知道,私生子D不知道)
protected internal:访问限于当前程序集或派生自包含类的类型。(A、B、C、D都知道,其他人不知道)
二、继承
- 继承类派生了基类(父类、超类)的 public 和 protected 特性和行为,并且能够任意添加和修改子类的特性和行为
- 通过在派生类(子类)名称后加冒号(:),冒号后为基类(父类)名称来实现继承
- 派生类能够调用基类的构造器(构造函数),调用形式是在派生类参数列表后添加一个冒号并通过带有关键字 base 来调用基类构造器
- 语法:
访问级别 ~~ class ~~ 派生类名:基类名
{
~~~~~ 类成员…
}
class People
{
/// <summary>
/// 姓名
/// </summary>
public string Name {
get; set; } //has a
private int top;
public People(int top)
{
this.top=top;
}
//仅仅本"家族"使用
protected int a;
}
class Teacher:People // is a
{
/// <summary>
/// 工资
/// </summary>
public int Salary {
get; set; }
private int left;//新的成员
public Teacher(int top,int left):base(top)//base调用基类构造函数,先走基类,再走派生类
{
this.left = left;
}
}
static void Main(string[] args)
{
//继承
//父类 只能使用 父类成员
People person1 = new People();
//子类 除了可以使用子类成员 还可以使用 父类成员
Student stu01 = new Student();
stu01.Name ="";
//父类型的引用 指向 子类的对象
//只能使用父类成员
People person2 = new Student();
person2.Name = "";
//如果需要访问该子类成员,需要强制类型转换
Student stu02 = (Student)person2;
//异常
//Teacher tea01 = (Teacher)person2;
//如果转换失败,不会抛出异常
Teacher tea01 = person2 as Teacher;
if(tea01 !=null)
tea01.Salary = 100;
}
注意:
1、访问修饰符 protected 可以使父类中的(私有)方法在子类中被调用,而不被外部其他类使用;
2、base作用:
1>访问基类的成员
2>访问基类的构造函数
-
里氏转换
---- 子类可以赋值给父类(如果需要的是父类,那么可以给一个子类代替)
---- 如果父类中装的是子类对象,那么可以将这个父类强制转换成“对应”的子类对象 -
new 关键字
---- 1、创建对象
---- 2、隐藏从父类那里继承过来的同名成员。隐藏的后果就是子类调用不到父类的成员。
三、多态
- 多态是同一个行为具有多个不同表现形式或形态的能力。在面向对象的编程范式中,多态性往往表现为“一个接口,多个功能”。
- 多态性可以是动态的或静态的。在静态多态性中,函数的响应是在编译时发生的。在动态多态性中,函数的响应是在运行时发生的。
- 设计思想:只有对象(而不是过程),知道如何执行一个特定的操作,通过规定这些操作的通用方式,利用共性促进代码重用
- 实现多态的三种手段:
1、抽象类
2、虚方法
3、接口 - 如果子类中的方法名与父类的方法名相同,那么子类会默认的将父类的方法给隐藏起来,解决方法有两个:
1、使用new关键字
2、使用虚方法 - 当父类中的方法不知道如何去实现的时候,可以考虑将父类写成抽象类,将方法写成抽象方法
1、静态多态性
在编译时,函数和对象的连接机制被称为早期绑定,也被称为静态绑定。C#提供了两种技术来实现静态多态性,分别为:
- 函数重载
- 运算符重载
2、动态多态性
动态多态性是通过 抽象类 和 虚方法 实现的。
1>抽象类
- C#允许使用关键字 abstract 创建抽象类,用于提供接口的部分类的实现。
- 语法:
访问修饰符 ~ abstract ~ class ~ 类名 (注意:前两个可换顺序) - 抽象方法语法:
abstract ~ 访问修饰符 ~ 返回值类型 ~ 方法名 (注意:前两个可换顺序)
注意:
- 抽象类建立了派生类的基础,但不能实例化一个抽象类对象(不能new自己);
- 如果类的任何一个方法标识为 abstract ,那么整个类都应该标识为 abstract,并且抽象方法没有方法的实现部分;
- 将抽象类的某个方法标识为 abstract ,表示继承自基类的每个派生类都必须实现自己的这个抽象方法。如果派生类没有实现抽象方法,那么派生类也必须是抽象类,并且不能被实例化;
- 与抽象类相对的是密封类,密封类修饰符:sealed 。作用:禁止任何类从该类派生(但密封类可以继承其他类),string类就是一个密封类;
- 如果父类的抽象方法中有参数,那么继承这个抽象父类的子类在重写父类方法的时候也必须传入对应的参数;如果抽象父类的抽象方法中有返回值,那么子类在重写这个抽象方法的时候也要传出返回值。
using System;
namespace PolymorphismApplication
{
abstract class Shape//抽象类
{
abstract public int area();//基类的抽象方法
}
class Rectangle:Shape
{
//字段
private int length;
private int width;
//构造函数
public Rectangle( int a=0, int b=0)
{
length = a;
width = b;
}
public override int area ()//派生类的抽象方法
{
Console.WriteLine("Rectangle 类的面积:");
return (width * length);
}
}
class RectangleTester
{
static void Main(string[] args)
{
Rectangle r = new Rectangle(10, 7);
double a = r.area();
Console.WriteLine("面积: {0}",a);
Console.ReadKey();
}
}
}
2>虚方法
- 当有一个定义在类中的函数需要在继承类中实现时,可以使用虚方法;
- 虚方法的关键字是 virtual ,虚方法可以在不同的继承类中有不同的实现;
- virtual 可用于属性和方法
- 语法:
访问修饰符 ~ virtual ~ 返回值类型 ~ 方法名 - 在派生类方法中,使用关键字 override 实现重写基类中的虚方法;
- 使用虚方法的步骤:
1、将父类的方法标记为virtual,表示这个父类的方法可以被子类重写
2、将子类的方法标记为override,表示重写父类的方法
using System;
namespace Day09
{
/// <summary>
/// 多态
/// </summary>
///
class Animal
{
public string Name {
get; set; }
public virtual void Shout() {
Console.WriteLine("未知叫声"); }//虚方法
}
class Dog:Animal
{
public override void Shout() {
Console.WriteLine(base.Name + ":汪汪"); }//复写虚方法
}
class Dog1:Animal
{
public new void Shout() {
Console.WriteLine(base.Name + ":汪汪"); }//不进行复写,使用自己方法
}
class Cat:Animal
{
public override void Shout() {
Console.WriteLine(base.Name + ":喵喵"); }//复写虚方法
}
class Program
{
static void Main(string[] args)
{
//Dog dog = new Dog() { Name = "小黑" };
//dog.Shout();
Animal animal = new Cat() {
Name = "小黑" };//类型转换
animal.Shout();
}
}
}
注意:
1、使用标识 new 表明该方法不是基类中虚方法的重写方法
四、小结
- 继承类派生了基类的public和protected特性和行为,并且能够任意添加和修改自己的特性和行为;
- 通过在派生类名称后加冒号,冒号后为基类名称来实现继承;
- 派生类能够调用基类的构造器,调用形式是在派生类参数列表后添加一个冒号并通过带有关键字base来调用基类构造器;
- 在基类中标识为virtual的方法能够被派生类重写,并且需要在派生类的该方法定义中使用关键字override。这是实现多态的关键:当在派生类上调用虚方法时,派生行为被调用;
- 派生类能够打破派生方法的多态性,但必须使用关键字new标识;
- 标识为abstract的方法没有方法实现部分,而只是提供了派生类必须重写的方法名称和签名。任何包含abstract方法的类都是abstract类,并且这样的类不能被实例化;
- 任何标识为sealed的类不能被派生;
- 抽象类不能实例化,抽象方法没有方法体;
- 如果父类中的方法有默认的实现,并且父类需要被实例化,这时可以考虑将父类定义成一个普通类,用虚方法来实现多态;如果父类中的方法没有默认实现,父类也不需要被实例化,则可以将该类定义为抽象类。