继承的特性:
1、单根性:一个类只能继承于另外一个类,不能同时继承多个类
2、传递性:子类的父类可以基于继承别的类,子类>父类>爷爷类>祖宗类,可以逐一传递祖宗类的属性和方法等,子类可以进行使用。
继承的好处:
1、代码重用,把重复的代码封装起来,别的类使用的时候直接继承即可
2、多态,通过里氏替换原则实现多态,增加程序的可扩展性和灵活性
一、继承的基本实现
Student person = new Student();
person.name = "张三";//继承父类的属性
person.sex = "男";//继承父类的属性
person.studentid = "001";
person.GetName();//继承父类的方法
Console.ReadKey();
/// <summary>
/// 人类
/// </summary>
class Person
{
public string name { get; set; }
public string sex { get; set; }
public void GetName() {
Console.WriteLine(name);
}
}
/// <summary>
/// 学生类 继承Person类
/// </summary>
class Student:Person//通过:类名 继承别的类
{
public string studentid { get; set; }
}
二、继承中构造函数问题
构造函数是不能被继承的
当一个子类继承父类之后,该子类中所有构造函数默认情况下,在自己被调用之前
都会调用一下父类中的无参构造函数,此时父类中没有无参构造函数的情况下就会报错,例如,如下代码编译器就会爆出错误。
StudentInfo StudentInfo = new StudentInfo("tom","合肥",1);
Console.ReadKey();
class PersonInfo
{
/// <summary>
/// 父类中的构造函数
/// </summary>
/// <param name="_Name"></param>
/// <param name="_Address"></param>
public PersonInfo(string _Name, string _Address)
{
this.Name = _Name;
this.Address = _Address;
}
public string Name { get; set; }
public string Address { get; set; }
}
class StudentInfo:PersonInfo
{
/// <summary>
/// 子类中的构造函数
///
/// 这个时候编译器会报错,当一个子类继承父类之后,该子类中所有构造函数默认情况下,在自己被调用之前
/// 都会调用一下父类中的无参构造函数,此时父类中没有无参构造函数的情况下就会报错
/// </summary>>
public StudentInfo(string _Name, string _Address, int _Id)
{
this.Name = _Name;
this.Address = _Address;
this.Id = _Id;
}
public int Id { get; set; }
}
解决方法一:再父类中添加一个无参数的构造函数
解决方式二:通过base在子类构造函数中调用父类有参数构造函数
StudentInfo StudentInfo = new StudentInfo("tom", "合肥", 1);
Console.ReadKey();
class PersonInfo
{
/// <summary>
/// 父类中的构造函数
/// </summary>
/// <param name="_Name"></param>
/// <param name="_Address"></param>
public PersonInfo(string _Name, string _Address)
{
this.Name = _Name;
this.Address = _Address;
}
/// <summary>
/// 解决方式一
/// </summary>
//public PersonInfo()
//{
//}
public string Name { get; set; }
public string Address { get; set; }
}
class StudentInfo:PersonInfo
{
/// <summary>
/// 子类中的构造函数
///
/// 解决方法二,通过base调用父类有参数构造函数,通过父类初始化Name和Address属性,然后子类构造函数初始化Id属性
/// </summary>>
public StudentInfo(string _Name, string _Address, int _Id):base(_Name,_Address)
{
this.Id = _Id;
}
public int Id { get; set; }
}
三、使用this调用构造函数
class PersonInfo
{
public PersonInfo(string _Name, string _Address)
{
this.Name = _Name;
this.Address = _Address;
}
/// <summary>
/// 这里可以通过this调用其他构造函数
/// </summary>
public PersonInfo(string name, string address, string city, int age) : this(name, address)
{
City = city;
Age = age;
}
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public int Age { get; set; }
}
四、访问修饰符
- public:所有地方都可以访问
- private:私有的,只有在本类中访问
- internal:只有再当前程序集中可以访问,exe,dll 程序集
- protected:当前类内部,和所有它子类的内部都可以访问
- protected internal:同时拥有internal 和 protected的访问权限,再子类内部也行,再程序集里面也行。或的关系
类的成员变量不写访问修饰符的情况下默认是private,类本身如果不写访问修饰符情况下默认是internal。
直接定义再命名空间中的类型访问修饰符只能是public或者internal。·
六、访问修饰符不一致问题
子类的访问修饰符不能大于父类的访问修饰符;
类中的方法的参数和返回值的不能小于类的访问修饰符;
类中的属性访问修饰符不能小于类的访问修饰符
class PersonInfo
{
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public int Age { get; set; }
}
/// <summary>
/// 当StudentInfo 为public,PersonInfo为internal 编译报错
/// </summary>
public class StudentInfo:PersonInfo
{
public int Id { get; set; }
}
class PersonInfo
{
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public int Age { get; set; }
}
/// <summary>
/// 当StudentInfo 为public,PersonInfo为internal 编译报错
/// </summary>
public class StudentInfo
{
public int Id { get; set; }
/// <summary>
/// 当属性的访问修饰符和类不一致时候编译器报错
/// </summary>
public PersonInfo Person { get; set; }
/// <summary>
/// 当方法参数和返回值访问修饰符和类不一致时候编译器报错
/// </summary>
/// <param name="personInfo"></param>
/// <returns></returns>
public PersonInfo Test(PersonInfo personInfo)
{
personInfo.Address = "111";
return personInfo;
}
}
7、里氏替换原则
我们可以使用父类类型去接受子类类型的对象。
Person person = new Student();//使用父类类型接受子类类型对象
8、子类重写从父类中的方法
父类中通过virtual定义虚方法,子类通过override关键字进行重写
person.name = "张三";//继承父类的属性
person.sex = "男";//继承父类的属性
//person.studentid = "001";
person.GetName();//继承父类的方法 没有重写之前调用父类的方法;重写之后调用的就是子类中的方法
Console.ReadKey();
/// <summary>
/// 人类
/// </summary>
class Person
{
public string name { get; set; }
public string sex { get; set; }
/// <summary>
/// 加上virtual关键字,子类可以重写父类中的方法
/// </summary>
public virtual void GetName()
{
Console.WriteLine("我是父类中的方法");
}
}
/// <summary>
/// 学生类 继承Person类
/// </summary>
class Student:Person//通过:类名 继承别的类
{
public string studentid { get; set; }
/// <summary>
/// 通过override关键字进行方法重写
/// </summary>
public override void GetName()
{
//base.GetName();调用父类中的GetName方法
Console.WriteLine("我是子类中的方法");
}
}
九、通过继承实现多态
多态,我的理解就是通过一句代码实现多种表现形态;举例:我这里有苹果、香蕉、橘子,这三种水果有他们各自的颜色,我想要获取出其中一种水果的颜色,普通写法代码下发如下:
/**
* 我这里有三个类Banana、Apple、Orange,我要分别显示他们的颜色,
* 我需要分别为他们new出对象,然后调用显示他们颜色的方法
*/
Banana banana = new Banana();
Apple apple = new Apple();
Orange orange = new Orange();
banana.MyColor();
apple.MyColor();
orange.MyColor();
Console.ReadKey();
class Banana
{
public void MyColor()
{
Console.WriteLine("我是黄色");
}
}
class Apple
{
public void MyColor()
{
Console.WriteLine("我是红色");
}
}
class Orange
{
public void MyColor()
{
Console.WriteLine("我是橙色");
}
}
以上这种写法也是可以,只不过假如以后我要增加一个水果,更换水中,调用的地方就不需要进行更改,new不同的对象即可。
Fruit fruit = new Apple();//这里只需要new不同的对象,就可以调用不同子类中实现
fruit.MyColor();//这里调用就不需要进行修改
Console.ReadKey();
class Fruit
{
public virtual void MyColor()
{
Console.WriteLine("我是水果,我不知道是什么颜色");
}
}
class Banana: Fruit
{
public override void MyColor()
{
Console.WriteLine("我是黄色");
}
}
class Apple : Fruit
{
public override void MyColor()
{
Console.WriteLine("我是红色");
}
}
class Orange : Fruit
{
public override void MyColor()
{
Console.WriteLine("我是橙色");
}
}
十、继承中base和this使用
在使用父类和子类属性的时候,base和this基本没啥区别,因为它们都指向同一个对象。但是当子类重写了父类中的方法时候,子类就可以通过base和this分别调用子类重写之后的方法和父类未重写的方法。
StudentInfo StudentInfo = new StudentInfo();
StudentInfo.Test2();
Console.ReadKey();
class PersonInfo
{
public string Name { get; set; }
public virtual void Test()
{
Console.WriteLine("我是父类!");
}
}
class StudentInfo: PersonInfo
{
public int Id { get; set; }
public void Test2()
{
//this.Test();//调用子类中重写父类的Test方法 输出:我是子类
base.Test();//调用父类中Test方法 输出:我是父类
}
public override void Test()
{
Console.WriteLine("我是子类");
}
}