c#软件开发学习笔记--面向对象
c#软件开发学习笔记
一、面向对象
面向过程:问题分解成一系列的步骤,然后按照顺序执行这些步骤
面向对象:就是把现实中的事物都抽象为“对象”。每个对象是唯一的,且都可以拥有它的属性与行为。我们可以通过调用这些对象的方法、属性去解决问题。
对象是类的一个实例化(即具体化)的结果。对象是具体的、实际存在的,可以在程序中被创建、操作和销毁。
类是对象的抽象,每个对象都属于某个类,它定义了对象所具有的属性和方法的结构和行为。对象通过实例化类来创建,并且可以根据类的定义进行属性和方法的访问和调用。
面向对象编程的三大特性(封装、继承、多态)
1.类
//类默认有一个访问修饰符 internal
class People
{
string name;
int age;
char sex;
void eat()
Console.WriteLine("吃饭");
void sleep()
Console.WriteLine("睡觉");
}
2.对象
//类的创建 创建出来一个对象(类的实例化)
//当我们创建一个类的同时,也会拥有一个数据类型,所以这个类实例化出来的对象,都属于这个类型
People p1 = new People();
3.属性和字段
字段和属性区别
1.字段一般给类和结构体内部使用,而属性一般给类或者结构体外部使用
2.字段一般是私有的,属性一般是公开的
3.属性有get\set访问器,字段可以理解为是类和结构中的一个常量/变量
4.字段可以存储数据,属性不能存储属性,只是操作拦截
public class People
{
//先添加一个私有的字段
//再添加一个公共的属性,可以添加get set访问器
private string _name;//字段 私有的字段 只能在内部访问
public string Name //属性
{
get
{
Console.WriteLine("get访问器触发了");
return _name;
} //在外部获取属性所关联字段值 触发get访问器 ,需要添加return 关联字段
set
{
Console.WriteLine("set访问器触发了");
if (value.Length==0) //属性拦截
{
throw new Exception("不能赋空字符串");
}
_name = value;
} //在给属性赋值的时候触发set访问器 把一个value赋值给字段
//属性语法糖写法
public string Id { get; set; }
}
4.构造函数
构造函数的作用:用于在对象创建的时候,进行初始化操作
class People
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsAlive { get; set; }
//在类中有一个默认的和类同名的函数,这个函数会在类实例化的时候 自动执行,我们称之为构造函数
//无参构造函数
public People()
{
}
//有参构造函数 构造函数本身是一个函数,可以定义形参接收参数,但是不需要也不能写返回值
public People(string a, int b)
{
this.Name = a; //this指向当前对象,可以省略
this.Age = b;
this.IsAlive = b >= 18;
}
}
5.析构函数
析构函数:是一个特殊的成员函数,当一个对象被释放的时候执行被释放: C#有垃圾回收机制(GC) 当一个数据没有被任何变量所引用的时候,垃圾回收机制就会把这个对象当作垃圾回收掉
析构函数:当类的某个对象被当作垃圾回收的时候,这个函数就会自动调用
析构函数的作用是释放对象的资源,但析构函数是又垃圾回收器控制的,无法做到显式调用,所以使用析构函数不是最佳方案(很少使用)
class People
{
//析构函数的定义: 一个和类同名的函数,在函数名前面加上~
~People()
{
Console.WriteLine("析构函数执行了");
}
}
6.静态static
static可以修饰:方法、变量、类
静态方法:
方便在没有创建对象的情况下来进行调用,只要类被加载了,就可以通过类名去进行访问
静态方法中不能访问非静态方法和变量,静态方法之间可以相互访问
静态变量:
静态字段,是属于类的,所有实例都会共享该字段(类名.静态字段名),普通字段是属于对象的,需要通过对象进行调用
7.常量和只读
readonly 表示只读的一个字段,只能在构造函数里面进行修改
静态的只读变量 只能在静态构造函数里面进行修改
public class People
{
public const int c = 30;//常量自动是 static 的,无需(也不能)使用 static 关键字
public readonly int a = 10;
public static readonly int b = 20;
public People()
{
a =20;
//b = 30; 静态只读不能在非静态的构造函数进行修改
}
static People() //静态构造函数只能被调用一次 1.创建类实例的时候调用 2.访问静态成员时候调用
{
b = 40;
}
}
二、继承
继承主要是为了减少代码的复用,如果有一个类的属性和方法想在另外一个类进行使用的话,可以通过继承实现属性和方法的复用
- 单继承:C# 只支持单继承,一个类只能直接继承一个父类
- 传递性:继承关系可以多层传递
- 所有类都继承自 Object:即使没有显式指定,所有类都隐式继承自 System.Object
继承的优点
- 提高代码的复用性
- 提高代码的维护性
- 让类与类之间产生关系,是多态的前提
继承的缺点
- 增强了类与类之间的耦合性,他们之间是紧密的 is-a 的关系
1.子类访问父类
在子类继承父类的时候 ,父类必须先创建, 创建子类的对象,会先调用父类的无参构造函数
即使子类调用带参数构造函数,默认情况下还是会调用父类的无参构造函数
如果想去调用父类有参构造函数 在子类有参构造函数后面添加base(参数)调用父类有参数构造函数
//父类构造函数
public People()
{
Console.WriteLine("1 父类的无参数构造函数");
}
public People(string name)
{
Console.WriteLine("2 父类带参数的构造函数");
}
//子类构造函数
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
定义:私有访问级别,只能在定义它们的类型内部访问。
应用:通常用于隐藏类型的实现细节,只能被同一个类或结构体的代码访问。
protected
定义:受保护访问级别,只能在定义它们的类及其派生类中访问。
应用:适用于那些只想在类的继承层次结构中暴露的成员。
internal
定义:内部访问级别,只能在同一个程序集内部访问。
应用:用于定义对其他程序集隐藏但对同一程序集中的其他类或成员可见的类型或成员。
protected internal
定义:受保护的内部访问级别,可以由同一程序集中的任何代码访问,或者由派生自该类的其他程序集中的代码访问。(并集)
应用:这是一个组合访问修饰符,提供了一种在程序集内部或通过继承在其他程序集中访问成员的方式。
private protected
定义:私有受保护访问级别,只能在其声明所在的类或该类的派生类中访问,并且只能在同一个程序集内部。
应用:这是C# 7.2中引入的较新的访问修饰符,它进一步限制了受保护成员的访问范围。
无修饰符(默认访问级别)
类的默认访问级别:如果没有指定访问修饰符,则类默认为 internal。
类成员的默认访问级别:如果没有指定访问修饰符,则类成员(包括字段、方法等)默认为 private。
| 访问修饰符/范围 | 当前类 | 子类 | 实例对象 | 引用当前项目的项目子类 | 引用当前项目实例对象 |
|---|---|---|---|---|---|
| public | √ | √ | √ | √ | √ |
| private | √ | × | × | × | × |
| internal | √ | √ | √ | × | × |
| protected | √ | √ | × | √ | × |
| protected internal | √ | √ | √ | √ | × |
| private protected | √ | √ | × | × | × |
三、多态
多态:同一个行为具有多个不同表现形式的能力
多态的分类
- 编译时多态(静态多态):通过方法重载和运算符重载来实现。
- 运行时多态(动态多态):通过继承和接口实现,以及方法重写来实现。
1.静态多态
重载是指多个方法名字相同、而参数列表不同的方法
重载允许返回不同的类型
1.方法重载
函数重载满足条件:
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带一个参数");
}
2.运算符重载
可重载与不可重载的运算符
| 运算符 | 可重载性 |
|---|---|
| +、-、!、++、– | 这些一元运算符可以进行重载 |
| <、>、==、!=、<=、>= | 这些二元运算符可以进行重载,需要注意的是某些运算符必须成对重载 |
| &&、|| | 无法重载逻辑运算符 |
| (type)var_name | 强制类型转换运算符不能重载 |
| +=、-=、*=、/=、%=、&=、|=、^=、<<=、>>= | 复合赋值运算符不能显式重载。 但在重载二元运算符时,也会隐式重载相应的复合赋值运算符,例如重载了+运算符也会隐式的重载+= |
| ^、=、.、?.、? : 、??、??=、…、->、=>、as、await、checked、unchecked、default、delegate、is、nameof、new、sizeof、stackalloc、switch、typeof | 这些运算符无法进行重载 |
//一个运算符的重载,其实就是一个方法,当对该类进行对应的运算符操作的时候,会自动执行这个重载的运算符方法
//格式 :
// public static 返回值类型 operator 要重载的运算符 (参数列表){}
public static double operator + (Box b1,Box b2)
{
return b1.Volume + b2.Volume;
}
2.动态多态
1.抽象类
修饰符: abstract
抽象类和普通类的区别
1.抽象类不能实例化,普通类可以实例化
2.抽象类可以有抽象方法,也可以有普通方法,普通的类不能有抽象方法
2.抽象类可以继承自抽象类,子类如果不是抽象类,就必须重写抽象类中全部的抽象方法
抽象方法特点
1.抽象方法只能定义在抽象类中
2. 抽象方法不能有方法体
3. 抽象方法就是用来被子类继承实现的,所以不能使用private修饰符
4. 子类如果不是抽象类,就必须使用override关键字实现所有继承的抽象方法
//定义抽象类
public 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" );
}
}
2.虚方法
关键字virtual
虚方法和抽象方法
1 虚方法可以定义在普通类中,抽象方法只能定义抽象类中
2 抽象方法不能有方法体,必须在派生类中实现,虚方法可以有方法体,可以不在派生类中实现,不实现则使用基类的方法
3.方法重写关键字
new 和 override 都可以实现对基类成员进行 “覆盖”
new 是隐藏基类中的成员 基类和派生类 各持一份 互不干扰 对象是什么类型 就是调用对应类型的方法
override 是覆盖 将会重写基类中的虚方法和抽象方法 不管对象是类型 都只有一个
3.类的分类
抽象类(abstract):抽象类就是指设计为被继承的类,抽象类只能被用作其他类的基类。
密封类(sealed):密封类只能用作独立的类,它不能用作基类。(不能被其他类所继承)
静态类(static):静态类中所有成员都是静态的,且不能被实例化。
部分类(partial):把一个类分成多个类文件进行定义,编译器在编译时候会把多个文件自动合并一个类。
更多推荐
所有评论(0)