c#继承、多态
·
继承
面向对象编程的三大特性**(封装、继承、多态)**
继承 允许我们基于现有类创建新类,实现代码重用
继承的特点
- 单继承:C# 只支持单继承,一个类只能直接继承一个父类
- 传递性:继承关系可以多层传递
- 所有类都继承自 Object:即使没有显式指定,所有类都隐式继承自 System.Objec
- //继承:主要是为了减少代码的复用,如果有一个类的属性和方法想在另外一个类进行使用的话,可以通过继承实现属性和方法的复用
//a类继承b类,b类叫基类或者父类,a叫子类或者派生类
//继承通过冒号实现继承,c#只能是单继承,也就是a继承b,b继承c,但是不能让a同于继承b和继承c
//继承不写冒号,默认是继承object,object类叫最终基类
static void Main(string[] args)
{
Student s1 = new Student();
s1.Name = "Test";
s1.Address = "s";
s1.F1();
People p1 = new People();
}
}
//定义父类
public class People
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
private int Age = 10;//私有的不能被继承,
protected int CC { get; set; }//受保护的属性 People的实例不能访问,子类可以使用
public void F1()
{
Console.WriteLine("父类的F1方法");
}
}
//学生类 继承于People
public class Student:People
{
public void F2()
{
Console.WriteLine(Name +":"+Id+":"+CC);
}
}
子类构造和父类构造执行顺序
static void Main(string[] args)
{
//People p1 = new People();
//People p2 = new People("福瑞");
//在子类继承父类的时候,父类必须先创建,创建子类的对象,会先调用父类的无参数的构造函数
//student s1 = new student();
//即使子类调用的是带参数的构造函数,默认情况下还是先调用父类的无参数构造函数
//如果想调用父类带参数的构造函数 在子类有参数构造函数后面添加 :base("11")调用父类有参数构造函数
Student s2 = new Student(12,"张三");
}
}
public class People
{
public string Name { get; set; }
public People ()
{
Console.WriteLine("1 父类无参数的构造函数");
}
public People(string name)
{
Console.WriteLine("2 父类带参数的构造函数");
}
}
public class Student:People
{
public int Age { get; set; }
public Student ():base()//base() base指的是父类的一个对象, :base()调用父类的构造函数
{
Console.WriteLine("3 子类无参数的构造函数");
}
public Student(int age,string n):base(n)//:base(n) 调用父类的有参数的构造函数
{
Console.WriteLine("4 子类带参数的构造函数"+Name);
}
}
属性访问修饰符
internal class Program
{
static void Main(string[] args)
{
//1.内部能不能调用 2.在外部使用实例去调用 3.子类(其他项目)能不能使用
//public 公共的 当前类 子类 实例 都可以访问,其他项目如果也继承了类,其他项目的子类也可以访问
//private 私有的 仅能在当前类内部访问 子类、实例都不能访问
//protected 受保护的 当前类以及子类(包括引用了当前项目的子类)
//internal 当前类 子类 实例 都可以访问 其他项目里都不能访问
//protected internal 能在当前类
People p = new People();
Console.WriteLine(p.a); //公共的 在外部可以通过实例访问
//Console.WriteLine(p.b); //不能被实例访问
//Console.WriteLine(p.c); //不能被实例访问
Console.WriteLine(p.d); //protected internal 对象可以访问
Console.WriteLine(p.e); //对象可以访问
}
}
public class People
{
public int a = 10;
private int b = 20;
protected int c = 30;
protected internal int d = 50;
internal int e = 40;
public void F1()
{
Console.WriteLine(a); //公共的 在内部可以访问
Console.WriteLine(b); //私有可以访问
Console.WriteLine(c); //可以访问受保护的
Console.WriteLine(d); //可以访问protected internal
Console.WriteLine(e); //可以访问internal
}
}
public class Man:People
{
public void F2()
{
Console.WriteLine(a); //公共的 在子类可以访问
//Console.WriteLine(b); //私有的 不能被子类访问
Console.WriteLine(c); //可以访问受保护的
Console.WriteLine(d); //可以访问protected internal
Console.WriteLine(e); //子类可以访问internal
}
}
| 访问修饰符/范围 | 当前类 | 子类 | 实例对象 | 引用当前项目的项目子类 | 引用当前项目实例对象 |
|---|---|---|---|---|---|
| public | √ | √ | √ | √ | √ |
| private | √ | × | × | × | × |
| internal | √ | √ | √ | × | × |
| protected | 能 | √ | 不能 | 能 | 不能 |
| protected internal | √ | √ | √ | √ | 不能 |
多态
多态:同一个行为具有不同的表现形式和能力
多态的分类
- 编译时多态(静态多态):通过方法重载(Method Overloading)和运算符重载(Operator Overloading)来实现。
- 运行时多态(动态多态):通过继承和接口实现,以及方法重写(Method Overriding)来实现。
静态多态:在编译过程中,通过方法重载和运算符重载来实现,也称之为静态绑定和早期绑定
动态多态:在运行过程中,通过方法重写,隐藏方法来实现,也称之为动态绑定或者后期绑定
函数重载
static void Main(string[] args)
{
//多态:同一个功能 不同的一个体现
//静态多态:函数重载和符号重载
//动态多态:通过继承接口 或者继承抽象类型实现动态多态
//能够体现多态的地方:函数重载
//函数重载满足条件:
//1.函数名必须一样
//2.参数的个数 类型不一样
//3.仅仅只有返回值类型不一样 不是重载
}
public static void F1()
{
Console.WriteLine("F1不带参数");
}
public static void F1(int a)
{
Console.WriteLine("F1带一个参数");
}
public static void F1(string a)
{
Console.WriteLine("F1带一个参数");
}
public static void F1(string a,int b)
{
Console.WriteLine("F1带两个参数");
}
public static void F1(int b,string a)
{
Console.WriteLine("F1带两个参数");
}
public static int F1(bool c)
{
Console.WriteLine("F1带1个参数");
return 10;
}
}
运算符重载
可重载与不可重载的运算符
| 运算符 | 可重载性 |
|---|---|
| +、-、!、++、– | 这些一元运算符可以进行重载 |
| <、>、=、!=、<=、>= | 这些二元运算符可以进行重载,需要注意的是某些运算符必须成对重载 |
| &&、|| | 无法重载逻辑运算符 |
| (type)var_name | 强制类型转换运算符不能重载 |
| +=、-=、*=、/=、%=、&=、|=、^=、<<=、>>= | 复合赋值运算符不能显式重载。 但在重载二元运算符时,也会隐式重载相应的复合赋值运算符,例如重载了+运算符也会隐式的重载+= |
| ^、=、.、?.、? : 、??、??=、…、->、=>、as、await、checked、unchecked、default、delegate、is、nameof、new、sizeof、stackalloc、switch、typeof | 这些运算符无法进行重载 |
注意:比较运算符必须成对重载,也就是说,如果重载一对运算符中的任意一个,则另一个运算符也必须重载。比如==和!=运算符、<和>运算符、<=和>=运算符。
internal class Program
{
static void Main(string[] args)
{
//符号重载:对运算符号或者比较符号等进行功能的重写 例如可以让两个对象能够实现相加
Box b1 = new Box(10,20,30);
Box b2 = new Box(30,40,50);
Box b3 = b1 + b2;
Console.WriteLine(b3.Length + ":" + b3.Width + ":" + b3.Height);
int b4 = b1 - b2;
Console.WriteLine(b4);
Box b5 = b1++;
Console.WriteLine(b1.Length);//11
Console.WriteLine(b5.Length);//10
Console.WriteLine(b1 > b2);//false
Console.WriteLine(b1 < b2);// true 执行<符号 执行重载函数,重载函数比较了b1和b2的体积, b1.v<b2.v成立 重载结果就为true
}
}
public class Box
{
public int Length { get; set; }//长
public int Width { get; set; }//宽
public int Height { get; set; }//高
public Box()
{
}
//体积的属性
public int Volume
{
get
{
return Length * Width * Height;
}
}
public Box(int l,int w,int h)
{
Length = l;
Width = w;
Height = h;
}
//重载加+ 实现两个对象相加 返回一个对象,结果对象三个属性分别是两个对象属性相加和
public static Box operator+(Box b1, Box b2)
{
//return b1.Length*b1.Width*b1.Height + b2.Length * b2.Width * b2.Height; 如果返回值是整型的方法
return new Box()
{
Length = b1.Length + b2.Length,
Width = b1.Width + b2.Width,
Height = b1.Height + b2.Height,
};
}
//重载减- ,两个对象相减等于两个体积相减的差值
public static int operator -(Box b1, Box b2)
{
return b1.Length * b1.Width * b1.Height - b2.Length * b2.Width * b2.Height;
}
//重载++ 让对象的长宽高在基础之上加一 并且返回一个对象
public static Box operator ++(Box b1)
{
return new Box()
{
Length = b1.Length+1,
Width = b1.Width+1,
Height = b1.Height+1
};
}
//重载大于号
public static bool operator>(Box b1, Box b2)
{
return b1.Volume > b2.Volume;
}
//重载小于符号
public static bool operator <(Box b1, Box b2)
{
return b1.Volume < b2.Volume;
}
}
抽象类
修饰符: abstract
特点:
- 不能被实例化
- 用于被继承(抽象类存在的意义就是作为基类,让其他的类所继承)
- 抽象类可以包含已经实现了的方法(普通方法) 属性 字段
- 还可以定义抽象方法/属性,强制子类必须实现特定的方法和属性
抽象方法特点:
- 抽象方法不能有方法体
- 抽象方法只能定义在抽象类中
- 子类必须使用override关键字实现所有继承的抽象方法
普通类和抽象类的区别
普通方法和抽象方法的区别
internal class Program
{
static void Main(string[] args)
{
//抽象类:
//定义时候使用abstract关键字,不能实例化
//抽象类存在的意义就是为了被继承的, 所以一般是公共的类
//可以定义普通属性和方法 也可以定义抽象属性和抽象方法,需要在子类把抽象的属性和方法进行实现
//抽象类可以继承自抽象类,子类如果不是抽象类,就必须重写抽象类中全部的抽象方法
// 抽象方法和普通方法区别
// 抽象方法只能定义在抽象类里面,普通方法可以定义在抽象类 也可以定义普通的类中
// 抽象方法没有方法体,普通方法需要有方法体
// 抽象方法在子类实现. 需要通过override重新抽象方法
}
}
//定义抽象类
abstract class People
{
public int Age { get; set; }//普通的属性
public void F2() // 普通方法
{
Console.WriteLine("F2非抽象方法");
}
public abstract string Name { get; set; } // 定义抽象属性
public abstract void F1(); //抽象方法 不要加{}
}
//定义子类继承抽象类 必须实现抽象属性和抽象方法
class Student : People
{
public override string Name { get; set; } = "ssss";
public override void F1()
{
Console.WriteLine("子类的F1");
}
}
虚方法
特点:
- 可以定义在普通的类中
- 可以有方法体
抽象方法和虚方法的区别?
- 抽象方法只能定义在抽象类中,虚方法可以定义在普通类中
- 抽象方法不可以有方法体,必须在派生类中实现,虚方法可以有方法体,可以不在派生类中实现,不实现则使用基类的方法
internal class Program
{
static void Main(string[] args)
{
Chinese c1 = new Chinese();
c1.Speak();
}
}
//虚方法可以定义在普通的类中 可以理解为一个类中的抽象方法,只不过这个方法可以在类中实现,也可以在派生类中重写
class People
{
public string Name { get; set; }
//给某个方法添加virtual 关键字 把这个方法修饰成虚方法(可以有方法体)
public virtual void Speak()
{
Console.WriteLine("打招呼");
}
}
class Chinese : People
{
public override void Speak()
{
Console.WriteLine("吃了吗?");
}
}
new和override
internal class Program
{
static void Main(string[] args)
{
// new 和 override
// override 关键字
// 作用: 用于在派生类中重写基类中的虚方法和抽象方法
// new 关键字
// 作用1: 生成实例对象
// 作用2:用于在派生类中 隐藏类中的成员
Man m1 = new Man();
Peope m2 = new Man();
m1.Test1();//Man_Test1
m2.Test1();//Peope_Test1
m1.Test2();//Man_Test1
m2.Test2();//Peope_Test1
m1.Test3();//Man_Test1
m2.Test3();//Man_Test3
// new 和 override 都可以实现对基类成员进行 "覆盖"
// new 是隐藏基类中的成员 基类和派生类 各持一份 互不干扰 对象是什么类型 就是用对应类型的方法
//override 是覆盖 将会重写基类中的虚方法和抽象方法 不管类型 都只有一个
}
}
class Peope
{
public void Test1()
{
Console.WriteLine("Peope_Test1");
}
public virtual void Test2()
{
Console.WriteLine("Peope_Test2");
}
public virtual void Test3()
{
Console.WriteLine("Peope_Test3");
}
}
class Man : Peope
{
//当派生类和基类中拥有同名的成员的时候,编译器会发出一个警告: XXXX隐藏继承的成员 XXXX如果是有一隐藏 请使用new关键字
public new void Test1()
{
Console.WriteLine("Man_Test1");
}
public new void Test2()
{
Console.WriteLine("Man_Test2");
}
public override void Test3()
{
Console.WriteLine("Man_Test3");
}
}
密封类和静态类部分类
internal class Program
{
static void Main(string[] args)
{
//Test t1 = new Test();
// Test1 t2 = new Test1();
//Test2 t3 = new Test2();
}
}
//抽象类型 不能实例化,用来被继承的
abstract class Test
{
}
//密封类 不能被继承 能够被实例化
sealed class Test1
{
}
//静态类要求成员都是静态的, 不能被实例化
static class Test2
{
public static int Age { get; set; }
}
//部分类:把一个分成多个类文件进行定义,编译器在编译时候会把多个文件自动合并一个类.
// C# 部分类partial class 是 C# 中的部分类,核心作用是:将一个类的代码拆分到多个文件中,编译时编译器会自动把这些文件合并成一个完整的类。
//它的关键字是 partial,必须和 class 配合使用。
// 每个文件中的类都必须用 partial 修饰
// 类名、命名空间必须完全一致
// 最终编译后,所有部分会合并为一个类
partial class Test3
{
}
更多推荐
所有评论(0)