面向对象
- 枚举类型是值类型, 读取速度要快很多.
- 因为是值类型所以可以和整形数相互转换.
- 注意: 枚举是和类平行的, 写的时候写在类的外面
enum Color {
red, Blue, Green, Yellow, 红
};
Color red = Color.红;
int a = Convert.ToInt32(Color.蓝)
OOP
- OOP = OOA + OOD + OOP
- 封装, 继承, 多态
类的成员
九个成员: 字段, 构造函数, 属性, 方法, 运算符, 索引器, 事件, 内部类.
1. 封装
封装字段
- 私有字段无法被类的外部直接访问, 所以需要封装成属性.
private int age;
public int Age {
get { return age;}
set { return age = value;}
}
- C# 3.0 之后出现了自动属性
- 只有属性, 没有字段.
public int Age {get;set;}
- 自动属性更简洁, 但是想要在 set 里面加条件判断的话, 需要用复杂点的那个.
public int Age {
get { return age;}
set {
if (value < 0 || value > 130) {
age = 20
}
// age = value;
}
}
疑问(未解决)
- 这里有一个疑问, 就是为什么不直接用 public 字段, 而是 private 字段封装成属性加 get , set.
- 感觉起到的效果差不多
- 唯一的区别就是, 如果属性不加 set, 可以设置为只读.
构造方法
-
作用: 实例化对象时, 对其进行初始化.
-
如果不定义, 就是默认无参构造方法.(基本要保证有一个无参, 一个有参)
-
特点:
- 与类同名
- 没有返回值关键字(void 也不行)
- 可以定义多个构造方法( 重载: 名字相同, 参数不同)
- this 关键字, 对当前实例对象的引用
- 私有构造函数不能创建对象(那有啥用呢)
析构方法
- 对象被销毁前执行, 做一些善后工作.
- 特点:
- 类只能定义一个析构方法.
- 无访问修饰符, 没有参数, 没有返回值.
- 波浪号开头.
- 对象销毁前, 自动调用.
~ Product() {
console.WriteLine("对象即将被销毁");
}
方法
- 首字母大写
参数
- 可缺省的参数: 参数可定义默认值
public void fn(int val = 5){}
-
输出参数: 当方法需要返回多个值, 类型可以不同
-
输出类型的参数需要初始化
-
可以用来获取商品信息
-
// 此时函数有 三个 返回值 可为 void
public string fn( out int i, out int j){
i = 0;
j = 0;
return "返回的字符串"
}
- params 参数: 参数的数量不确定
- 前面可以定义参数
- 后面不允许有其他参数
- params 关键字只能有一个
// params
public int fn(params int[] nums) {
int sum = 0;
foreach(var item in nums) {
sum += item;
}
return sum;
}
int res = fn(2, 3, 4, 5);
- ref 引用
- 修饰参数按地址传递, 默认都是按值传递
// ref 来完成 a 和 b 的互换
public void fn(ref int a, ref int b) {
int temp;
temp = a;
a = b;
b = temp;
}
类为元素的数组
Student s1 = new Student();
Student s2 = new Student();
Student[] students = { s1, s2 };
2. 继承
-
好处: 实现代码的重用.
-
类只能是单继承, 一个类只能有一个父亲.
-
子类的名称的最后一部分就是父类的名称.
-
子类构造函数加 base 关键字: 在子类中, base 代表对父类的引用, 也包含列祖列宗.
- 如果是子类的子类在引用父类的时候, 不用写 base.base.
-
子类构造函数的参数需要包含自身的属性加上父类的属性.(字段也可以, 主要是完成初始化)
- 父类参数不加变量类型
-
和父类一样, 同样需要两个构造函数
- 无参对无参, 有参对有参
public class UniStudent : Student
{
public string Major { get; set; }
public string Group { get; set; }
public UniStudent() : base()
{
}
public UniStudent(int id, DateTime birthday, string name, string major, string group)
: base(id,birthday,name)
{
this.Major = major;
this.Group = group;
}
}
-
修饰符 base 关键字用来修饰父类的字段.
-
只有子类或者子类的子类可以访问和修改.
public void fn()
{
base.pint = 0;
}
-
internal 只能在一个命名空间内使用, 同一个解决方案里面不能跨命名空间使用.
-
protected internal 组合修饰符
-
所以一共有五类访问修饰符: private public protected internal, 组合.
虚方法
-
在父类中定义的,专门给子类去重新设计方法的功能
-
应该是类似 java 的抽象方法?
-
在子类中对父类中的虚方法进行重写 override
- virtual 和 override 需要成对出现
-
主要作用就是规范子类的方法名称, 大型项目管理常用.
public class Graph {
public double[] Length {get;set;}
public virtual double GetLength() { return 0;}
public virtual double GetArea() { return 0;}
public Graph(int length) {
this.Length = new double[length];
}
}
public class Rectangular {
public Rectangular(int length, int width, int height) : base(length) {
base.Length[0] = height;
base.Length[1] = width;
}
public override double GetLength() {
return 2 * (base.Length[0] + base.Length[1]);
}
public override double GetArea() {
return base.Length[0] + base.Length[1];
}
}
new 关键字
- 子类用来隐藏父类的同名方法
- 在使用多态的时候, 用子类来创建父类对象
- 如果调用子类同名重写的方法, 就是直接调用子类方法
- 如果调用子类同名 new 方法, 就是调用父类的方法.
密封类
- sealed 关键字: 父类使用 sealed 关键字, 无法产生子类, 太监.
- 出于安全性考虑, 有时候会用密封类
静态
- static 关键字: 可以修饰类, 字段, 属性, 方法, 构造函数.
- 静态方法用的最多, 可以在静态类, 也可以在实例类(public)
- 特点:
- 为所有的类所共享 ( 猜着是对一个命名空间内的所有类所共享 ).
- 静态构造函数无论实例化多少次, 只会调用一次. 先调用静态构造函数, 再调用实例构造函数.
- 静态方法可以通过 类名.方法名 来调用, 不能通过实例名调用.
- 实例类可以定义静态方法.
- 声明周期和应用程序的声明周期是一致的, 而实例类一段时间后就会自动销毁对象并释放内存.
- 静态方法只能只能访问方法内的字段, 不能访问方法外类里定义的字段和实例方法.
- 实例方法内可以访问同一个类中的静态方法
- 静态类里面所有的成员都是静态的. 静态类的效率很高.
- 不能实例化
- 所有成员都被全部对象共享
抽象类 (abstract)
-
抽象方法 (abstract method): 只有声明, 没有定义, 没有大括号.
-
也可以有普通成员, 非抽象的字段或者方法, 静态也可以.
-
但抽象方法不能存在普通类中.
-
抽象类不能被实例化.
-
抽象类的子类(非抽象)必须要重写全部的抽象类的方法.
- 即使子类是抽象类, 也都要实现
- 要写 override 关键字
-
子类如果是抽象类, 则不用重写所有的抽象类.
接口 (interface) : 引用类型
- 抽象类里可以有非抽象的成员, 但接口内的成员全部是抽象的.
- 不能被实例化.
- 成员默认都是 public , 但是不能写任何的访问修饰符.
- 方法只能声明不能定义, 不能写大括号
- 所有的方法必须被子类实现
- 多继承, 可以有多个父级
- 如果同时继承类和接口, 要先写父类, 再写接口
public interface IMobile {
void Insert();
}
public interface INet {
void Net();
}
public interface IIntelMobile {
void Pay();
void Chat();
}
public interface IIPhone : Inet, IMobile, IIntelMobile{
}
显式接口
多个接口中包含相同方法的声明, 同名同参, 而同时被一个类去实现.
public interface Ichinese {
void Speak();
}
public interface IUSA {
void Speak();
}
public class Person : Ichinese, IUSA {
void Speak() {
};
void IChinese.Speak() {
};
void IUSA.Speak() {
}
}
public void Main(string[] args) {
// 多态向上转型调用不同接口中同名同参的方法
IChinese ic = new Person();
ic.Speak();
IUSA iu = new Person();
iu.Speak();
}
抽象类和接口的区别
- 抽象类有构造方法, 接口没有
- 抽象类中有实例成员, 接口中没有, 全部抽象
- 抽象类中可以有静态方法, 接口中不能定义静态方法
- 抽象类是单继承, 接口是多继承
- 类可以实现多个接口, 但只能继承一个抽象类
- 类必须实现接口的全部成员, 抽象子类可以实现 0 或者多个父类抽象成员
3. 多态
- 方法的重载 (overload)
- 重写 (override)
- 向上转型: 父类引用
// 向上转型 父类 Graphic 可以是抽象类 和 接口
Graphic g = new Circle(10);
//调用子类的方法
g.GetArea();
- 向下转型:
Mobile mobile = new IntellMobile();
IntellMobile mobile2 = (IntellMobile)mobile; // 向下转型
IntellMobile mobile2 = mobile as IntellMobile; // 向下转型
mobile2.Message(); // 调用的是 intellmobile 的方法
- 父类作为方法的参数