【c#基础】9.面向对象
什么是面向对象?
面向对象:就是把现实中的事物都抽象为“对象”。每个对象是唯一的,且都可以拥有它的属性与行为。我们就可以通过调用这些对象的方法、属性去解决问题。
什么是对象?
对象是面向对象编程中的一个概念,它是类的一个实例化(即具体化)的结果。对象是具体的、实际存在的,可以在程序中被创建、操作和销毁。
什么是类?
每个对象都属于某个类,类是对象的抽象,它定义了对象所具有的属性和方法的结构和行为。对象通过实例化类来创建,并且可以根据类的定义进行属性和方法的访问和调用。
为什么非要创建对象才能使用呢?
类只是一种程序内的“设计图纸”或者摸具,需要基于图纸或摸具生产实体(对象),才能正常工作这种套路,称之为:面向对象编程,类就相当于是闹钟的设计图纸,而对象就相当于按照闹钟的设计图纸所生产出来的闹钟。
一、类和对象的创建(封装)
(1)对象的创建
类名 对象名 = new 类名()
对象名.成员名称=值
new的时候{成员名称=值}(2)获取成员的值
对象名.成员名称(3)方法(对象的行为)调用
对象名.方法名()
internal class Program
{
static void Main(string[] args)
{
//类只是提供一个类型,凡是由类构建出来对象都属于同一种类型
//对象创建 需要使用构造函数进行创建
People zs = new People();
zs.id = "002";//访问公共的属性
People ls = new People();
ls.id = "001";
//调用成员方法
//调用非静态的方法 (对象方法)
zs.F1();
ls.F1();
//调用静态的方法(类方法) 类名去调用
People.F2(10);
}
}
//类的创建位置:在namespace范围内.
//创建语法:
//修饰符:public 公共的 ,private私有的 protected受保护的,internal内部访问,类一般使用public 或者internal
//class 关键字
//类名:大驼峰命名法则
//类里面包含成员变量:字段 属性 方法
public class People
{
//字段又叫成员变量
//定义成员变量的语法
//修饰符 类型 变量名;
string name;//字段
int age;//字段
bool sex;//字段 默认情况下字段是私有的
public string id;//字段
private int _height;//
//成员方法: 修饰符 static/非static void/数据类型 方法名(参数列表){}
public void F1()
{
Console.WriteLine("非静态无返回值方法");
}
public static int F2(int a)
{
return 100000;
}
}
二、字段和属性
1.字段和属性区别
字段和属性区别:
(1)用途不同:字段是内部使用;属性是外部使用。
(2)权限不同:字段一般是私有的;但属性一般是公开的。
(3)结构不同:字段是存数据的变量;属性带有get/set访问器,本身不存数据,只是对字段的访问包装。
(4)安全性不同:字段直接暴露数据,不安全、不可控;属性包装字段,可以加验证、逻辑,安全、可控。
(1)私有字段:
_开头 + 小驼峰private int _age;(2)公开属性:大驼峰
public int Age { get; set; }(3)赋值 = 自动跑 set
(4)读取 = 自动跑 get
(5)set 里可以写逻辑、调用方法等,赋值时自动执行
(1)同个类内,方法中直接写属性名,就能访问当前对象的属性,编译器会默认加上
this.(2)
this代表当前实例对象,this.属性名,this可以省略。什么时候必须写 this. ?
当局部变量 / 参数 和 属性重名时,必须用
this.区分,否则编译器会优先找局部变量;
无命名冲突:
属性名/this.属性名都可以,随意写;有命名冲突:必须加
this.区分属性和局部变量 / 参数;
internal class Program
{
static void Main(string[] args)
{
//创建对象
People p = new People();
p.Name = "T"; //在给属性赋值的时候触发set访问器
Console.WriteLine(p.Name);//取值的话触发get访问器
People p3 = new People();
p3.Name = "Y";
// p3.Msg = "S"; //只能读取 不能修改
Console.WriteLine(p3.Name+":"+p3.Id);
}
}
public class People
{
private int _age;// 私有的字段 只能在内部访问
//最早的属性定义方式
//先添加一个私有的字段
//再添加一个公共的属性,可以添加get set访问器
private string _name;//字段
public string Name //属性
{
get{
Console.WriteLine("get访问器触发了");
return _name;
} //在外部获取属性所关联字段值 触发get访问器 ,需要添加return 关联字段
set{
Console.WriteLine("set访问器触发了"+value);
if (value.Length==0)
{
throw new Exception("不能赋空字符串");
}
_name = value;
} //在给属性赋值的时候触发set访问器 把一个value赋值给字段,
}
//2 优化上面属性的写法
private int _a1;
public int A1 { get => _a1; set => _a1 = value; }
//3如果不想加属性拦截 get set不想写任何东西
//属性语法糖写法
public string Id { get; set; } = "001";
// 可以保留一个get 使属性变成只读的属性
public string Msg { get; }
}
2.属性拦截例子
namespace _8属性拦截例子
{
internal class Program
{
static void Main(string[] args)
{
Dog d1 = new Dog();
d1.Age = 19;
Console.WriteLine(d1.Age);
d1.Color = '白';
Console.WriteLine(d1.Color);
}
}
}
namespace _8属性拦截例子
{
internal class Dog
{
public int a=100;
private int _age;
public int Age
{
get
{
_age *= 10;
return _age;
} //get访问器 对象.属性
set
{
if (value < 0||value>20)
{
throw new Exception("年龄不在合适的范围内");//
}
_age = value; //年龄在0-20之间
}//set访问 对象.属性=10
}
private char[] colors = { '黄', '白', '黑' };
private char _color;
public char Color
{
get { return _color; }
set
{
if (!colors.Contains(value))
{
throw new Exception("颜色不合适");
}
_color = value;
}
}
}
}
三、构造函数
在 C# 中,构造函数的名称必须与类名一致,并且它不能具有返回类型(如void或int)。它在类中的地位比较特殊,不需要我们主动调用,当创建一个类的对象时会自动调用类中的构造函数。
默认地,所有类都有构造函数:如果你不自己创建类构造函数,C# 会为你创建一个。但是,那么你将无法为字段设置初始值。
注意: 类中默认有一个无参构造函数,但是如果手动添加了有参构造函数后,就会把无参的构造函数覆盖掉
namespace _9构造函数
{
internal class Program
{
static void Main(string[] args)
{
//构造函数:People() 和类的名称是一样的.作用创建对象和初始化属性的值, 可以有非静态的构造函数,也可以有静态构造函数
//创建一个类默认提供一个不带参数构造函数,也可以定义有参数的构造函数.
People p1 = new People();
Console.WriteLine(p1.Name);//张三
People p2 = new People();
Console.WriteLine(p2.Name);//张三
People p3 = new People("李四",20);
Console.WriteLine( p3.Name);
People p4 = new People("王五", 20);
Console.WriteLine(p4.Name);
}
}
public class People
{
public string Name { get; set; }
public int Age { get; set; }
//不带参数的构造函数
public People()
{
Name = "张三";
this.Age = 10; // this当前类的对象,不是具体的对象 ,
}
public People(string n, int a)
{
this.Name = n; //把参数赋值给属性
this.Age = a;
Console.WriteLine(this.Name + "---------");
}
}
}
四、析构函数
(1)析构函数:是一个特殊的成员函数,当一个对象被释放的时候执行
(2)被释放:C#有垃圾回收机制(GC) 当一个数据没有被任何变量所引用的时候,垃圾回收机制就会把这个对象当作垃圾回收掉
(3) 析构函数:当类的某个对象被当作垃圾回收的时候,这个函数就会自动调用(4)析构函数的作用是释放对象的资源,但析构函数是由垃圾回收器控制的,无法做到显式调用,所以使用析构函数不是最佳方案(很少使用)
namespace _10析构函数
{
internal class Program
{
static void Main(string[] args)
{
People p = new People();
p = null;
GC.Collect();//GC强制回收,对象不再被引用的时候 进行回收
Console.ReadKey();
}
}
public class People
{
//当一个对象不再被其他变量引用的时候,可以通过GC垃圾回收机制强制回收,这个时候会触发析构函数
~People()
{
Console.WriteLine("对象被GC回收了");
}
}
}
五、静态成员(属性、方法)
1.静态与非静态区别
(1)归属不同 :静态成员属于类;非静态成员属于实例对象。
(2)调用方式不同: 静态成员通过 类名.成员 调用,无需实例化;非静态成员必须先
new创建对象,再通过 对象名.成员 调用。(3)内存特点不同: 程序运行时只分配一次内存,全局唯一,所有对象共享同一份数据;非静态成员每创建一个对象,就单独分配一份内存。
(4)访问规则不同 :静态方法只能使用静态的属性和静态方法(若要访问实例成员,必须先实例化对象);非静态方法不仅能使用静态属性、静态方法,还能直接使用实例属性、实例方法。
简洁版:
(静态属于类,非静态属于对象)
(静态不用 new,非静态必须 new)
( 静态全局只有一份,非静态每个对象独立一份)
(静态只能使用静态,非静态全都能用)
为什么是这样?
- 静态:属于类,程序一运行就存在
- 非静态:属于对象,必须
new才存在静态方法运行时,对象还没创建呢,所以根本用不了非静态!
什么时候用?
- 静态:工具方法、共用数据、全局唯一
- 非静态:每个对象不一样的属性 / 方法(姓名、年龄、成绩等)
internal class Program
{
static void Main(string[] args)
{
//非静态找对象去调用
People p1 = new People();
p1.F1();
p1.Id = 1;
//静态的方法 类名.属性/方法
People.Age = 10;
People.F2();
}
}
//静态属性定义在类空间里面,每一个实例都拥有同一个静态数据
//非静态属性 每个实例拥有对立一份数据
public class People
{
//非静态成员变量和非静态成员方法
private int id;
public int Id {
get
{
return id;
}
set
{
Console.WriteLine("ssssssssss");
id = value;
//Id = value; // Id是属性 属性=10执行set访问器 切记不要写属性赋值,造成死循环
}
}
public void F1()
{
Console.WriteLine("非staticF1");
//非静态的方法可以使用静态属性和静态方法
// Age = 10;
// F2();
}
//静态成员变量和静态成员方法
public static int Age { get; set; }
public static void F2()
{
//如果直接调用非静态的方法 不允许的,但是如果使用new 创建一个新的 ,这个新的对象可以调用
//People p = new People();
//p.F1();
//p.Id = 1;
//静态方法只能使用静态的属性和静态方法
Console.WriteLine("StaticF2");
}
}
2.静态方法测试
internal class Program
{
static void Main(string[] args)
{
People p1 = new People();
p1.AddNum();
People p2 = new People();
p2.AddNum();
//Num属性的静态 每个实例都拥有同一个数据
//Num属性的非静态 每个实例都有一个独立的一份数据
Console.WriteLine(p1.GetNum());// 如果静态num值是2,非静态num值1
Console.WriteLine(p2.GetNum());// 如果静态num值是2,非静态num值1
}
}
public class People
{
public int Age { get; set; }
public int Num { get; set; }
public void AddNum()
{
Num++;
}
public int GetNum()
{
return Num;
}
}
六、常量和只读变量
readonly :表示只读的一个字段,只能在构造函数里面进行修改;
public static readonly:静态的只读变量,只能在静态构造函数里面进行修改;
静态构造函数只能被调用一次:1.new的时候调用,2.访问静态属性时候调用;
const :给字段加const 关键字 ,表示这个字段为常量,不能修改;
internal class Program
{
static void Main(string[] args)
{
People p1 = new People();
//p1.a = 30; //不能其他进行修改
// Console.WriteLine(p1.a);//可以访问
Console.WriteLine(People.b);
Console.WriteLine(People.c);
}
}
public class People
{
//readonly 表示只读的一个字段,只能在构造函数里面进行修改,
public readonly int a = 10;
public static readonly int b = 20;//静态的只读变量 只能在静态构造函数;里面进行修改
public const int c = 30;//常量,自动编译成一个静态的常量
public People()
{
// b = 30; 静态只读不能在非静态的构造函数进行修改
a =20;
Console.WriteLine(a+"++++++++++++");//20
}
// 静态的构造函数不能使用修饰符
//静态构造函数只能被调用一次 1:new的时候调用了,2访问静态属性时候调用
static People()
{
b = 40;
Console.WriteLine(b+"----------------");
Console.WriteLine("静态构造函数只能被调用一次 1:new的时候调用了,2访问静态属性时候调用 ");
}
}
七、继承
- 作用:继承主要是为了代码复用,把公共的属性和方法放到父类,子类直接使用,不用重复写。
- 叫法:A 类继承 B 类,
- B 叫 父类 / 基类
- A 叫 子类 / 派生类
- 语法:C# 用 冒号
:实现继承。- 单继承:C# 是单继承,
- 可以:A → B → C(链式继承)
- 不可以:A 同时继承 B 和 C
- 默认继承:如果不写冒号,类默认继承 object。
- object:是所有类的最终基类(祖宗类)。
internal class Program
{
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 = 1000; //私有的不能被继承
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);
}
}
1.子类构造和父类构造
- 在子类继承父类时,创建子类对象(不管无参 / 有参),会先调用父类的无参构造函数,再调用子类自己的构造函数;
- 如果想主动调用父类的有参构造,必须在子类构造函数后面加 : base (参数);
- 一旦加了 : base (参数),父类无参构造就不会再执行了。
- 父类的构造函数,一次只会执行一个! 要么执行无参,要么执行有参,不可能两个都执行!
internal class Program
{
static void Main(string[] args)
{
// People p1 = new People();
// People p2 = new People("福瑞");
//在子类继承父类的时候,创建子类的对象(无参或有参),会先调用父类的无参构造函数,在调用子类自己
// 的构造函数。
// Student s1 = new Student();
//1 父类的无参数构造函数
//3子类的无参数构造函数
//即使子类调用带参数构造函数,默认情况下还是先调用父类的无参数构造函数
//如果想调用父类有参构造函数 在子类有参数构造函数后面添加base("11"),调用父类有参数构造 。
Student s2 = new Student(10,"张三");
//2 父类带参数的构造函数
//4子类带参数的构造函数
}
}
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()调用父;类的构造函数
{
Console.WriteLine("3子类的无参数构造函数");
}
public Student(int age, string n) : base(n) //:base(n) 调用父类的有参数构造函数
{
Console.WriteLine("4子类带参数的构造函数"+Name);
}
}
2.访问修饰符
| 访问修饰符/范围 | 当前类 | 子类 | 实例对象 | 引用当前项目的项目子类 | 引用当前项目实例对象 |
|---|---|---|---|---|---|
| public | √ | √ | √ | √ | √ |
| private | √ | × | × | × | × |
| internal | √ | √ | √ | × | × |
| protected | √ | √ | × | √ | × |
| protected internal | √ | √ | √ | √ | × |
八、多态
多态的分类
编译时多态(静态多态):通过方法重载(Method Overloading)和运算符重载(Operator Overloading)来实现。
运行时多态(动态多态):通过继承和接口实现,以及方法重写(Method Overriding)来实现。
静态多态:在编译过程中,通过方法重载和运算符重载来实现,也称之为静态绑定和早期绑定
动态多态:在运行过程中,通过方法重写,隐藏方法来实现,也称之为动态绑定或者后期绑定
一、函数重载
函数重载满足条件:
(1)函数名必须一样;
(2)参数的个数 或者 类型不一样;
(3)仅仅只有返回值类型不一样,不是重。
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine();
}
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 b)
{
Console.WriteLine("F1带1个参数");
return 10;
}
}
二、运算符重载
符号重载:对运算符号或者比较符号等进行功能的重写 例如可以让俩个对象能够实现相加
核心规则:
必须用
public static必须用
operator关键字必须返回值(不能是 void)
二元运算符(+、-、*、/)必须带两个参数
固定写法:public static 返回值 operator 运算符(参数列表)
| 运算符类别 | 可重载运算符 | 关键备注 | |
|---|---|---|---|
| 算术运算符 | +、-、*、/、% |
二元运算符,必须带 2 个参数 | |
| 一元算术运算符 | ++、--、+、-、~ |
一元运算符,仅需 1 个参数 | |
| 比较运算符 | ==、!=、>、<、>=、<= |
必须成对重载(如重载==必须同时重载!=) |
|
| 位运算符 | &、` |
、^、<<、>>` |
支持按位运算自定义逻辑 |
| 特殊运算符 | true、false |
用于自定义类型的布尔判断 |
| 运算符类别 | 不能重载的运算符 |
|---|---|
| 赋值运算符 | = |
| 逻辑与 | && |
| 逻辑或 | || |
| 三元运算符 | ?: |
| 类型判断 | is |
| 类型转换 | as |
| 获取大小 | sizeof |
| 获取类型 | 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
//执行<符号 执行重载函数,重载函数比较了b1和b2的体积, b1.v<b2.v成立,重载结果就为true
Console.WriteLine( b1<b2); //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)
抽象方法和普通方法区别:
(1)抽象方法只能定义在抽象类里面,普通方法可以定义在抽象类,也可以定义普通类中;
(2)抽象方法没有方法体,普通方法需要有方法体;
(3)抽象方法 / 属性必须在子类重写(override)实现,普通方法则不必须。
抽象类和普通类区别:
(1)抽象类:必须加 abstract; 普通类:不需要关键字;
(2)抽象类:不能 new 对象; 普通类:可以直接 new 对象;
(2)抽象类:可以有抽象方法 + 抽象属性; 普通类:绝对不能有任何抽象成员
(3)抽象类:普通子类继承后,必须重写(override)所有抽象成员; 普通类:子类继承后,不需要重写任何东西,直接用;
(5)抽象类:普通属性、普通方法、抽象属性、抽象方法; 普通类:只能写普通属性、普通方法。
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" );
}
}
四、虚方法(virtual)
抽象方法与虚方法区别:
(1)抽象方法:abstract,虚方法:
virtual;(2)抽象方法只能定义在抽象类中,虚方法普通类、抽象类都可以定义;
(3)抽象方法没方法体,子类必须重写;虚方法有方法体,子类可选重写。
(4)虚方法和new 或者override进行联合使用
internal class Program
{
static void Main(string[] args)
{
China c = new China();
c.SayHellow();
}
}
public class People
{
public string Name { get; set; }
public virtual void SayHellow()
{
Console.WriteLine("People打招呼");
}
}
public class China :People
{
//父类的虚方法 需要在子类通过override进行重写,覆盖父类的方法
// 也可以不用重写,调用的父类的方法
public override void SayHellow()
{
Console.WriteLine("吃了吗,抽个烟");
}
}
public class Japan : People
{
//父类的虚方法 需要在子类通过override进行重写,覆盖父类的方法
// 也可以不用重写,调用的父类的方法
public override void SayHellow()
{
Console.WriteLine("汪汪");
}
}
public class HanGuo : People
{
//父类的虚方法 需要在子类通过override进行重写,覆盖父类的方法
// 也可以不用重写,调用的父类的方法
public override void SayHellow()
{
Console.WriteLine("啊你赛有");
}
}
五、new 和 override
在重写虚方法的时候 可以使用new 或者override
new 或者override 区别new:创建一个新的方法,父类和子类都保留了一份,他们是相互独立,调用new重写方法时候,就看变量的类型:变量类型如果是父类,调用父类的方法,如果是子类,调用子类自己的方法。
override:相当于把父类的方法覆盖掉了,不管变量类型是子类类型还是父类类型,调用的是子类的方法
1. new(隐藏 / 独立)
- 父类、子类各存一份方法,互不干扰
- 调用谁,看变量类型 :变量是父类 → 调父类, 变量是子类 → 调子类
2. override(重写 / 覆盖)
- 父类方法被覆盖
- 调用谁,看真实对象是谁 :不管变量怎么写,永远调用最终子类的方法
internal class Program
{
static void Main(string[] args)
{
// 创建的对象如果使用自己类型的变量去接收, 调用的时候调用自己类的方法
//People p = new People();
// p.Test1();
// p.Test2();
// p.Test3();
People p1 = new Student(); // 找父类类型的变量 接收子类的对象
Student s1 = new Student(); //找自己类的类型变量接收对象
p1.Test1();//People的Test1
s1.Test1();//Student的Test1
p1.Test2();//People的Test2
s1.Test2();//Student的Test2
p1.Test3();//Student的Test3
s1.Test3();//Student的Test3
}
}
public class People
{
public virtual void Test1()
{
Console.WriteLine("People的Test1");
}
public virtual void Test2()
{
Console.WriteLine("People的Test2");
}
public virtual void Test3()
{
Console.WriteLine("People的Test3");
}
}
public class Student : People
{
public new void Test1()
{
Console.WriteLine("Student的Test1");
}
public new void Test2()
{
Console.WriteLine("Student的Test2");
}
public override void Test3()
{
Console.WriteLine("Student的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
{
}
}
七、重载和重写区别
| 特点 | 重载 (Overload) | 重写 (Override) |
|---|---|---|
| 位置 | 同一个类 | 父子类(继承) |
| 方法名 | 相同 | 相同 |
| 参数 | 必须不同 | 必须相同 |
| 返回值 | 无关 | 必须相同 |
| 关键字 | 无 | virtual + override |
更多推荐



所有评论(0)