C#进阶语法**总结
## 目录
### 类(定义、对象、类和对象使用、属性、抽象类、静态成员、静态类、密封类、构造函数、私有构造函数、析构函数、this关键字、索引器、索引器重载);三大特征(封装、继承、多态);接口;命名空间;异常处理
**机器视觉开发的 “灵魂”:不学这些 = 你永远只能写简单代码,做不了真正的视觉项目**
###### 视觉里干什么?
(类、对象、属性、构造函数):相机、光源、算法工具、参数配置、设备控制类。 --所有视觉 SDK、相机、图像算法,**全是类 + 对象**。
(静态成员、静态类):全局日志、全局配置、工具方法(图像处理、坐标转换)、相机公共方法。 --视觉项目里 80% 工具类都是静态类
三大特性:
(封装):把相机操作封装起来,外部只调用 `Open()` `Shot()`
把算法封装起来,外部只调用 `Process()`
代码安全、不乱、不崩溃
工业视觉最看重的就是封装!
(继承):海康相机、大恒相机、迈德思相机 → 统一继承一个父类
代码结构超级清晰
换相机不用重写代码
大型视觉项目必用继承
(多态):不同相机调用同一个方法名 `Capture()`
不同算法调用同一个方法名 `Run()`
多态 = 视觉插件化架构核心
接口(超级重要):定义相机规范
定义算法规范
定义设备通讯规范
模块化、插拔式开发
机器视觉架构 90% 都靠接口!
抽象类:定义相机模板。
定义算法模板。
强制子类必须实现某些功能。
抽象类 = 视觉框架的骨架
密封类、this、索引器:安全控制。
防止类被继承乱改。
设备数据集合访问。
属于进阶规范,工业代码必写
构造函数、析构函数:相机初始化。
释放相机资源。
释放内存。
防止内存泄漏、相机卡死。
视觉项目最容易崩的地方,全靠它稳住。
异常处理(try-catch):相机打不开;抓拍失败、算法报错、设备断开、图像为空。
没有异常处理 = 视觉软件一运行就崩。
命名空间:引用海康、大恒SDK;引用图像处理库;模块化管理代码。
**不用命名空间,你根本无法调用视觉库**
学了能做:相机控制、图像处理、光源控制、算法流程、工业软件、机器视觉上位机。
## 进阶语法
C#类进行抽象。
定义类:关键字class 默认internal 一般是public,默认internal 通过class声明定义一个类名 一个或多个成员变量
属性名一般和字段名称相对应,属性名一般是大写。字段名类似变量名首字母小写。
对应属性完整封装写法。
定义一个或多个成员方法。 没有返回值类型用void,有就指定返回值类型。
简单类:成员变量、成员属性、成员方法 (比较复杂:事件、索引器、构造函数、析构函数--一般不怎么用来定义。)
指定类和成员方法访问修饰符。
属性:实际值存在运行字段里面。自动属性:编译器去创建。
对象也叫类的实例,用new关键字
创建对象--实例化这个类。
对象地址存变量里面。引用类型对象存堆里。
引用类型变量不是对象。 了解基本方式。
实例化一个对象,声明另一个对象,实例化对象赋给它。通过任何一个对象访问里面属性,调用里面方法。
实例化两个course类型对象,对这个对象进行简单的赋值,course1和course2也一样。
通过类去创建对象,通过对象操作类中的属性、方法、字段,最后可以通过变成处理实现想要结果。
初始化--赋值--调用成员方法--输出信息。
属性:类、结构体、属性、字段都可以包含的成员。
通过属性读写类或字段变量的值。
get访问器获取字段的值,set访问器修改字段的值。 获取值 修改内部私有字段的值。 只读只写(可少,都是可读可写)
包含get和set的都是属性。
给属性赋值就是修改值。
自动属性:未显示自定义要读写的字段。
定义属性三种方式:完整封装,自动封装,get set自定义逻辑。 get出现return这条语句之前逻辑(获取它本身的值然后再把他本身的值转换处理,处理后返回处理后的值。一般没有自定义检查或处理逻辑吧自定义字段的值返回回去就行。
普通类不能定义抽象成员。
给一个入口获取字段和私有字段的值就是属性。
抽象类才能创建属性。抽象类不能声明密封类。
如果一个类是抽象的,在他里面成员可以再子类里面进行实现。
**封装** → 字段、属性、访问修饰符
**继承** → **父类、子类、abstract**
**多态** → 重写、virtual、override
非抽象类中不能定义抽象属性,抽象方法也一样。
抽象类不能使用new关键字进行实例化一个抽象类。 AbPeople student=new Student();
抽象类定义基本规则(关键字+成员定义)
实现类(派生类)写冒号 派生于AbPeople
实现抽象成员:属性和方法 只要抽象成员都能实现属性和方法
实例化抽象对象通过实现类中的派生类创建实例,给成员赋值,调用成员方法执行的方法还是实现类里面实现逻辑。

静态成员
一个类中:字段、方法、事件都叫静态成员。 函数就是方法 普通类可以定义实例成员,也可定义静态成员。静态类只能是静态成员。
索引器和析构函数是实例成员 静态成员访问直接通过类访问**不需要** new 实例对象。
静态变亮、属性--类成员,不是实例成员
静态成员通过类访问,不能通过实例成员访问。
静态变量也叫字段。
类名.属性名/变量名
静态变量:泛型、集合、数组 通过类名访问静态成员
**静态方法 不能直接访问 非静态(实例)成员**
**静态方法 只能访问 静态成员**
局部变量可以定义并使用,不能定义静态局部变量。 静态属性会少。
静态对局部无效,方法内部不能定义静态变量。 静态是类成员不是局部变量。
静态方法也是静态成员,通过类名调,
如果在外部调另一个类,里面静态方法通过类名调。
**静态 对局部变量无效**
**方法内部 ➡️ 不能定义静态变量**
**静态只能写在类层级,不能写在方法里**
静态类里面的类成员也必须是静态的。
界面输入信息大都是字符串类
数字字符串转换成整形通过类名调
静态成员是类成员。
实例构造函数:非静态
静态构造函数只能构造一次。
静态类中所有成员都必须是静态的。
封装通用处理类就是通用处理方法。
密封类:不能被派生。不能被继承、不能被派生。
构造函数:不能主动调用的一类方法。创建类的对象时候会自动调用。
实例构造:带参和不带参。 多用于属性初始化,里面其他成员也可以。类里面字段封装成属性更安全。
用new创建初始化类中变量和函数。
类中封装成属性更安全。
构造访问修饰符public
任意成员属性或变量进行初始化。
无法在外部实例化:私有构造函数。
如果没有显示定义构造函数,默认有一个无参构造函数。当用new创建对象时,就会执行无参构造函数。
string默认值是null
带参构函提供定义就可以用。
如果一个静态类有静态属性通过非静态类构建。如果一个非静态类的静态属性构建也通过静态类。
如果类中定义了显示构造函数,里面可以没有主题(方法),里边没有逻辑也可以有逻辑。
默认没有显示带参构造函数,可以不定义无参构造函数,如果显示定义带参,必须要显示定义一个无参,除非不需要通过无参构造创建。如果仍然允许通过无参构造,必须得显示定义一个,要不然会报错。如果只有带参构造函数,没有不带参构造函数,实例化时候想要调用无参,就不会隐式创建无参。除非没有构造函数才会隐式创建,如果有构造函数没有无参就会隐式创建无参。
一个类可以带参可以无参,可以调用。
静态构造函数只会执行一次,会在实例构造之前执行。
静态方法不能访问非静态成员。
第二次执行实例构造函数,不会执行静态构造。
不能使用访问修饰符,不能使用参数--静态构造函数
类或结构体只能有一个带参构造函数
无参不能继承或重载。
不能直接调用,只能CLR调用,自动调用。
先执行再访问(先执行后调用) 创建实例在构造函数之前。
静态构函特征:只执行一次;最先执行。实现谁在谁之前调用。(创建第一个实例或引用静态成员之前)。理解特性几点。
通过md文档来执行,清楚调用点、调用方式、特征。 静态类普通类很少用静态构造,普通类、非静态类只需要执行一次操作可以在里面执行。练习跑一下
嵌套类基本不用。
外部无法构建实例。
都可以初始化成默认值。
这个对象已经创建实例唯一一个,不同时创建多个不同对象。另外创建爱你一个实例通过方法访问。
私有静态成员为空调用私有构函创建
第一次调用方法
第二次调用方法直接返回。
单利效果:程序运行期间如果没有重复释放之前不能创建,第一次创建后执行构造返回实例,第二次再来构造给返回回去。两个对象一个实例。**全局只允许 1 个实例**,第一次 `new` 才创建对象、执行构造;**第二次及以后调用,直接返回已有实例,不会新建,两个引用共用一个对象**。(登陆了不能重复登录)
重复多次实现,用公开构函不用私有(嵌套类基本不用)
私有构造:不能在外边直接调用。
重复多次创建实例化,用公开不能用私有。
构造函数是私有,不能调用。 静态构函从内部返回一个私有实现。
析构函数
不在释放资源做一些清理工作一般不用。
析构函数只能在类中定义,不能用于结构体。
前面加~ 无返回值
只有类类型才能包含析构函数。
构造函数new时候会自动调用。 析构函数释放时候调用。 主要做清理工作。 释放就能实现这个接口。
创建实例--释放资源--退出系统
以后析构函数几乎不用。(知道析构函数,了解基本特征)
今后面试会问析构函数。
释放时候才去调。
释放资源后做一些清理(垃圾回收工作)。
this关键字主要是成员变量和成员属性--串联构造函数定义类的索引器。
通过this访问成员变量。
构造函数没有继承一说,this表串联。、、
访问类里面集合定义索引器。
用this关键字修饰的方法叫扩展方法。
包含索引器必须是list集合列表。
字符串也可以作为索引器。
扩展方法:类是静态类,扩展方法是静态方法。
this关键字-扩展方法
索引器用中括号
索引器允许重载,声明一个类可以声明多个索引,可以是字符串类型。
遍历不能同时对它赋值。
哪一个名称与索引号一样--索引器重载
针对集合类型通过访问集合类型元素可以用。(编号索引+字符串索引)
子类访问修饰符不能高于基类
C#只支持单继承,不支持多继承。
以I开头的不支持。
接口定义一系列方法成员,而且
不支持实现多个基类,但可以实现多个接口,后面可以跟一个或多个接口,基类只能有一个,
只能有一个class基类,但可以实现一个或多个接口。--多重继承。
C#多重继承通过接口来。
一个类可以实现一个或多个接口。
什么叫继承 继承派生类与子类是什么 单继承 继承传递 C#如何实现多重继承
方法重载:多态
静态多态:调用时直接绑定。
方法重载:方法名相同、参数个数或参数类型不同、返回值可以不同可以相同。
想调用方法:对应方法参数列表传对应方法实参列表。
运算符重载方式掌握,前提封装一个重载方法。
虚方法与覆写
与继承相关
虚方法重写:先定义一个基类
虚方法在基类里面定义。
重写虚方法
封装一个属性就可以不重写
如果有也可以没有,必须声明主体。
运行时多态,不管创建谁,最后都执行子类重写的方法。
非虚方法覆写:覆盖基类虚方法。
运行时多态也叫动态多态。
抽象方法只能在抽象类中定义,类似接口中定义的方法。
虚方法:要有方法体,方法体是空的,抽象方法不是。重写抽象方法的方法必须是实打实方法。
抽象方法不能有方法体。
抽象方法不能有实现,虚方法可以有实现,哪怕是一个空的,也必须有方法主题。可以做到与虚方法一样的结果。抽象类中也可以定义虚方法,在普通类或其他类也可以实现。子类在抽象类中必须实现。
abstruct叫抽象方法。隐式虚方法。抽象方法的方法体不是空的。
重写抽象方法必须是实打实方法。
抽象类通过派生类实例化调用派生类实例。
如果派生类没有重写,可以不重写,调用基类中的方法。如果重写调用派生类中的方法。通过派生类中定义,调用还是派生类中的方法。
接口:不管怎么做,只管能做什么。
类:单继承,利用接口可以实现多继承。
不用I开头也支持。但可读性不强。
接口主要是行为约定,包含方法定义。
接口主要定义方法
接口不能直接实例化对象。
抽象的不能实例化。
接口是功能上的抽象。 语法合同,是一个约定,规定能做什么而不管怎么做,需要实现的功能,由派生类实现怎么做功能。
接口用一个类实现接口,
哪怕抽象类也要实现。
记住这个结构,接口是功能上抽象,定义接口,实现接口。
接口:规定一系列能做什么而不能怎么做。派生类实现怎么做功能。
接口好处:体现封装,具体暴露前端就是给他一个接口。供别人查看,不暴露封装。
定义一个Add方法好处:**定义规则**
一个接口可以继承另一个接口,**抽象类也完全可以继承另一个抽象类(或者普通类)**。
ShowInfo接口和Login接口
一个接口继承另一个接口就具有另一个接口的方法。
接口和抽象类区别:方法定义都一样:只能包含定义,不能包含实现。
接口和抽象类定义的方法都是抽象。 抽象类和接口都不能被实例化。
都可以包含方法声明和方法定义。 派生类实现。
抽象类抽象方法不能被实现,非抽象类可以实现。
接口主要定义方法,进行方法声明。非抽象方法可以实现。
一个类可以实现多个接口,抽象类只能被单一一个类实现。抽象类只能被单一继承。
一个是抽象方法定义,一个是行为方法定义。
行为特性就是接口。
行为和特征抽象选择抽象类,只注重行为选择接口。除非注重上不重视特征,只重视行为抽象,就是接口。
抽象方法可以被实现,非抽象方法不能被实现。
一种是类抽象,一种是功能抽象。
同一命名空间类名必须唯一,不同命名空间下可以有同名的。如果出现冲突性情况,可以加上命名空间来解决冲突问题。如果类型引用报错和其他命名空间又没有其它相同名称冲突问题,只需要引用命名空间就行。
两个中,一个显示,一个引用命名空间名指代。
遇到冲突没加会报错,两个冲突问题不指代,加上命名空间名。两个引用之一。(多个命名空间有相同类名就这样)清楚命名空间规范就可以
**写全前缀**(`MySpace1.UserInfo`)→ 用的就是**前缀里的命名空间**
**不写前缀**(`UserInfo`)→ 用的就是**上面 `using` 引入的命名空间**
异常处理:保证程序运行中不会发生错误。
有try后会跟catch或finally语句块(跟一个或多个)
不中断程序在上层UI层调用层面捕获异常不抛,下层不得不在调用方法内部去处理异常捕获到就直接处理向上抛的过程。
try...catch可能会出现finally
exception(ex)所有异常基类。
执行最后逻辑finally 不管catch最后都会执行finally
throw:UI层很少抛异常,一般放在被调用的方法里面


出现新的异常包装秤异常后往上抛
针对性异常捕获做针对性异常处理。
笼统性异常处理Exception处理 和针对性异常处理 分条针对性处理:C#异常处理两种方式
调用方法内部做异常捕获也不要做
## 1. 直接抛(就地抛出、就地处理)
在当前方法里,**自己抛、自己抓**,问题不往外传。
### 代码示例
```
public void Test()
{
try
{
int a = 0;
int res = 10 / a;
}
catch
{
// 直接抛:当前方法内部抛出
throw;
}
}
```
✅ 特点:
- 异常停在当前方法
- 自己解决 / 自己触发崩溃
- 简单粗暴,不麻烦上层
------
## 三、2. 向上抛(抛出给调用者、逐层上抛)
当前方法**不处理**,把异常**抛给调用它的方法**,交给上层处理。
### 语法:方法加 `throws` 思想(C# 不用 throws,靠 throw 往上丢)
```
// 本方法不捕获,异常向上抛
public void Test()
{
int a = 0;
int res = 10 / a; // 出错,自动向上抛
}
```
### 手动向上抛写法
```
public void Test()
{
try
{
int a = 0;
int res = 10 / a;
}
catch
{
// 向上抛:交给调用我的人处理
throw new Exception("运算出错");
}
}
```
✅ 特点:
- 当前方法不背锅、不处理
- 异常传递给**上层调用方**
- 多层调用时,一层层往上传
------
## 四、核心区别(考试 / 面试必背)
| 类型 | 操作 | 处理方 | 适用场景 |
| :----- | :---------------- | :------- | :--------------------------------- |
| 直接抛 | `throw;` 本地抛出 | 当前方法 | 错误本地解决、直接终止 |
| 向上抛 | 抛给调用者 | 上层代码 | 底层方法只干活,异常交给业务层处理 |
------
## 五、一句话绝杀记忆
- **直接抛**:自己犯错自己扛
- **向上抛**:自己不处理,甩给上一级
## 1. 直接抛(就地抛出)
**自己捕获、自己扔、自己结束**
```
try
{
// 出错代码
}
catch
{
throw; // 直接抛,当前代码终止
}
```
👉 作用:当前方法处理不了,**当场报错**。
------
## 2. 向上抛(抛出给调用方)
**下层不处理,丢给上层方法处理**
```
public void A()
{
B();
}
public void B()
{
// 出错不捕获,自动向上抛给 A
}
```
手动向上抛:
```
catch
{
throw new Exception("错误"); // 抛给调用我的方法
}
```
------
## 核心口诀(必背)
1. **直接抛**:自己抓,自己扔,本地崩溃
2. **向上抛**:下层甩锅,上层捕获处理
