封装
类的关键字
声明位置
成员方法
构造函数
- 名字和类名一致
- 可以实现函数的重载
- 默认有一个无参的构造函数
1 2
| People p1 = new People();
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class People { public string Name; public int Age;
public People() { Name = "Unknown"; Age = 0; }
public People(int age) : this("Unknown", age) { Name = "Unknown"; Age = age; }
public People(string name, int age) : this() { Name = name; Age = age; } }
|
析构函数
- 当垃圾真正被回收的时候,才会执行的函数:
~People() { }
垃圾回收机制 (GC)
- 垃圾回收的过程中遍历堆(Heap)上动态分配的所有对象
- 通过识别它们是否被引用来确定哪些对象是垃圾,哪些对象仍被引用
- 所谓的垃圾就是没有任何变量、对象引用的内容
注意:GC 只负责堆(Heap)上的垃圾回收(因为引用类型在堆上存储),栈(Stack)上的由系统自动管理。
原理:0、1、2 代内存
- 所有的新对象都会被分配在 0 代内存 中,0 代满时触发 GC。0 代中非垃圾的对象会移到 1 代内存 中;1 代满了,会触发 0、1 代的 GC,并将非垃圾的对象放到 2 代内存 中;2 代满了,就会触发 0、1、2 代的 GC。
注意:不会对大对象进行搬迁,85KB(85000 字节)以上的对象为大对象,直接存储在 2 代内存里。
成员属性
基本概念:
- 用于保护成员变量
- 为成员属性的获取和赋值添加逻辑处理
- 解决 3P(Public、Private、Protected)的局限性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| public class Student { public string name; public bool sex; public int age; public float C_Score; public float Unity_Score;
public Student(string name, bool sex, int age, float C_Score, float Unity_Score) { this.name = name; this.sex = sex; this.age = age; this.C_Score = C_Score; this.Unity_Score = Unity_Score; }
private float Sum { get { return C_Score + Unity_Score; } }
public void SayHello() { if (sex) { Console.WriteLine($"我叫 {name},今年 {age} 岁了,是男同学,我的总成绩是:{Sum},平均成绩是:{Sum / 2f}"); } else { Console.WriteLine($"我叫 {name},今年 {age} 岁了,是女同学,我的总成绩是:{Sum},平均成绩是:{Sum / 2f}"); } }
public int Age { get { return age; } set { if (value > 150 || value < 0) { Console.WriteLine("年龄必须是 0-150 岁之间"); } else { age = value; } } }
public float Score { get { return C_Score + Unity_Score; } set { if (value > 100 || value < 0) { Console.WriteLine("成绩必须是 0-100 之间"); } else { C_Score = value; } } } }
|
索引器
使对象可以像数组一样访问:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| class Person { public string Name { get; set; } public int Age { get; set; } public List<Person> friends = new List<Person>();
public Person(string name, int age) { Name = name; Age = age; }
public Person this[int index] { get { if (friends == null || index >= friends.Count) { return null; } return friends[index]; } set { if (index < 0) { throw new IndexOutOfRangeException(); } if (index >= friends.Count) { friends.Add(value); } else { friends[index] = value; } } } }
static void Main(string[] args) { Person p1 = new Person("Alice", 25); Person p2 = new Person("Bob", 24); Person p3 = new Person("Charlie", 23); p1.friends.Add(p2); p1.friends.Add(p3); Console.WriteLine("{0}, {1}", p1[0].Name, p1[1].Name); Console.WriteLine("{0}, {1}", p1[0].Age, p1[1].Age); }
|
静态成员(static)
特点:可以通过类名访问,与程序同生共死。
静态构造函数
- 特点:不能使用访问修饰符、没有参数、只会调用一次。
- 作用:初始化静态成员。
拓展方法
注意:只能写在静态类里。
为已有类添加新方法,而无需修改原类定义。
1 2 3 4 5
| public static void SayHello(this string name, int age) { Console.WriteLine("这是我为 string 拓展出的一个方法,我叫 " + name + ",今年 " + age + " 岁!"); }
|
这样就为 string
类拓展出了一个 SayHello()
的成员方法,当你实例化一个 string
类型的对象时,就可以通过该对象调用此方法:
1 2
| string name = "John"; name.SayHello(18);
|
运算符的拓展
让自定义类和对象能够像基本类型一样进行运算。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class Point { public float x; public float y;
public Point(float x, float y) { this.x = x; this.y = y; }
public static Point operator +(Point p1, Point p2) { return new Point(p1.x + p2.x, p1.y + p2.y); }
public void Show() { Console.WriteLine("坐标为(" + x + ", " + y + ")"); } }
|
作用:让自定义的类和对象实现运算。
继承
里氏替换
所有的父类都可以用子类来表示,用父类容器来装子类对象:
1 2 3 4 5 6 7 8 9 10 11 12 13
| class GameObject { }
class Enemy : GameObject { }
class Item : GameObject { }
GameObject[] objects = { new Enemy(), new Item(), new GameObject() };
|
is 和 as
1 2
| Item item1 = new Item(); GameObject gameObject = new GameObject();
|
is
:判断一个对象的类型是否是对应的类,例如:item1 is GameObject
返回 True
。
as
:转换,例如:item1 as GameObject
将 item1
转换成 GameObject
类型对象(如果无法转换则返回 null
)。
继承中的构造函数
1 2 3 4 5 6 7 8 9
| public class Monster : Player { public Monster(string Name, int HP, int Attack, int Defence) : base(Name, HP, Attack, Defence) { } }
|
特有名词解释
多态
virtual、override、base
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Duck { public virtual void Say() { Console.WriteLine("嘎嘎嘎"); } }
class WoodDuck : Duck { public override void Say() { base.Say(); Console.WriteLine("吱吱吱"); } }
|
对父类方法的重写,实现多态(同一个函数在不同的对象上有不同表现)。
抽象类(abstract)
特点:
- 不能被实例化
- 可以包含抽象方法
- 继承抽象类必须用
override
重写所有抽象方法
抽象函数
特点:
- 只能在抽象类中声明
- 没有方法体
- 不能是私有的
- 继承后必须用
override
重写
接口(interface)
- 接口不包含成员变量
- 只包含方法、属性、索引器、事件
- 成员不能被实现(只有声明)
- 成员可以不用写访问修饰符,但不能是私有的
- 接口不能继承类,但可以继承另一个接口