C#高级语法总结
C#高级语法学习内容泛型反射委托、Lambda表达式、事件、特性以及Linq扩展线程与进程异步编程等。一、泛型用处极大上位机 视觉通用统一封装相机类、设备类、检测结果类一套代码适配不同相机 / 不同产品数据集合存储检测数据、良品不良集合、参数集合封装通用串口 / 网口收发工具类不用重复写重载数据库通用增删改查所有视觉数据通用操作总结写通用工具类必备减少重复代码二、反射动态加载不同视觉算法类切换检测方案不用改代码动态读取配置文件 (XML/JSON) 自动赋值实体参数插件式架构外接检测模块、相机驱动动态加载上位机动态绑定参数面板自动生成调试界面场景多机型通用视觉软件必备三、委托 Lambda 事件委托视觉检测回调结果拍照完成、检测完成触发回调设备动作回调气缸到位、传感器信号到位执行视觉抓拍通用算法委托灵活切换匹配 / 测量 / 识别函数Lambda快速筛选图像数据、筛选 NG 产品、筛选日志简化 Halcon/OpenCV 图像遍历、坐标筛选界面简洁绑定事件代码更短事件最常用相机拍照完成事件检测异常报警事件PLC 信号触发拍照事件上位机界面按钮、状态变更、日志推送事件视觉项目核心通信触发全靠事件四、特性 (Attribute)给视觉参数加标注参数范围、是否调试可见、单位注释配置文件自动序列化忽略字段权限特性限定管理员才能修改视觉阈值日志自动标记检测类型、工位编号多用于规范项目、大型视觉框架五、Linq 扩展方法快速筛选不良品数据、重复产品、异常图像批量处理图像坐标、点位数据排序去重简化集合查询、统计良品率、产量统计写视觉工具扩展方法图像转灰度、ROI 快速截取日常写代码最高频极度提升开发速度六、线程 进程线程视觉重中之重采集线程单独线程持续取相机图像不卡界面检测线程后台跑算法上位机界面流畅不卡顿多工位视觉一个线程对应一个工位检测日志写入、数据保存单独开线程不阻塞流程进程主视觉程序 独立相机采集进程分离第三方视觉程序 (Halcon 工具、标定工具) 调用外部进程防止算法崩溃直接卡死整个上位机没有多线程 做不了工业实时视觉项目七、异步编程 async/await现在主流异步读取相机图像、异步读写数据库异步串口 / 网口收发 PLC 数据不阻塞主线程异步保存图片、异步导出报表大图像预处理、深度学习推理异步执行新一代视觉上位机全部用异步淘汰老多线程写法精简求职优先级必背最高频必精通委托、事件、Lambda、Linq、多线程、异步做视觉上位机 90% 场景在用常用必会泛型、XML/JSON 序列化、扩展方法进阶项目用反射、特性、进程一句话总结整套体系界面事件触发 → 异步 / 多线程采图 → LambdaLinq 处理图像数据 → 委托回调算法结果 → 泛型统一设备管理 → 反射动态配置参数 → 异步存库上报 PLC这就是完整机器视觉上位机开发逻辑细讲泛型基础讲解很实用也用的多。自带泛型类list和Dictionary进阶语法用Winform讲。输错不会报错 TryParse逻辑的特点处理逻辑相同只是目标类型不同而已。泛型特点/用用场景--只要符合就能用逻辑相同或相似只是传入参数类型或返回值类型不同而已。object是所有类型基类也是泛型。不管值类型还是引用类型都可以调用GetType方法这个方法就是返回实例。一种泛指不指代具体类型。泛型达到极致必须和反射配合。传入方法参数可以推断。泛型类取反泛型方法不同类型需求用同一个方法解决。定义泛型方法带T类型方法类型参数 里面写方法。传入参数是不确定类型习惯用T里面写过程泛型方法具有通用性。不针对具体逻辑。提供对象把对象代入进去。传入实参能自动推算返回值类型是类型参数传入类型不能直接推断。定义某一类型封装泛型类参照具体类而言把内部方法由逐个转变为泛型过程直到所有类型都以类型参数来代替的过程。有些类型对当前类中的方法不适用不适用的情况下给一个范围限定。定义泛型方法 1.设计哪些类型。 2.适当给些约束值类型无效给引用类型约束值类型约束引用类型不可去限定值类型约束。 3.会存在基类派生子类情况。可以针对不同类型用同一个泛型类来处理。针对不同类用泛型类封装类里面封装细节基本一样。ArrayList 也要了解重要相对于固定数组更加灵活方便存取。建议使用list列表ArrayList可以解决固定长度数组。里面项是object不推荐使用。针对某一个项可以用但不推荐。添加一个值类型有装箱和拆箱损耗不推荐使用。ListT类型集合列表强类型转换但不会有性能损耗。添加修改移除都可以操作。不建议使用这个集合操作里面项不是强类型是弱类型。添加项取出来有装箱和拆箱性能损耗。引用类型是某一个class类型。拆箱、移除项、判断某一个项是否在集合里面 获取某一个项的位置索引 集合排序功能 翻转功能从里面指定位置的数目翻转到指定功能。索引号移除里面的项是强类型不是弱类型任意一个项都可以往里面加。List数组作为什么用它就做什么用。数据集合存储为什么选list不选数组里面元素操作尤其是项的增减用list列表更灵活更好操作。泛型集合列表通过索引访问对象。强类型列表类型集合一样是安全的。比Arraylist好类型安全。存储值类型遍历集合中可以改值但不能移除里面的项会报错因为在枚举过程中。添加项在指定位置插入一个项添加一个集合添加多个项移除项移除指定位置处的项判断某个项是否存在查找某个指定项查找某个项指定位置索引查找符合条件的项索引查找某个指定项位置对这组信息排序针对这种值类型)集合中的项前后翻转清空这个集合中的项。常见添加项插入项添加一组移除项移除指定位置的项判断存在Find指定项查找某个项所在位置偶尔涉及符合项指定条件位置后面三个涉及到存在。思考List什么时候用。颜色变淡可以不写可以省略。--自动推断。泛型类型t规则理解性记忆。描述准确就行。DictionaryTKeyTValue泛型集合类--键值对集合强类型集合重要键和值一一对应。使用常见。每次都是成对添加。键和值可以是字符串可以是值类型对象集合。键和值可以是值也可以是字符串可以是整形也可以是对象。使用频率比较高的集合。判断是否存在最后一个添加对应的值不是统一类型才用哈希表。一一对应关系用键值对集合。泛型约束更严谨加泛型约束。5种约束限定泛型约束范围。限定类型范围--泛型约束基类约束也是引用类型约束。无参构造在最后一个约束。值类型约束加where限定类型范围自带引用类型必须是class泛型类型指定类型本身就是基类约束。调用是限制指定类型实际范围--泛型约束。掌握约束规则。基类约束实际调用类型约束必须是基类。 值类型约束与引用类型约束不能共存。基类约束和引用类型约束不能同时共用。 接口约束可以和基类约束共存。 定义泛型方法用基类约束也可以。几个相似类里面和引用方法里面适用就行。T派生struct类里面类型一样使用方法差不多才用引用类型和值类型约束。基类约束限制类型参数实际类型本身必须是基类本身或它的子类或派生类。这个类型才能用于基类。应用类型派生于类型本身或它的应用。基类约束不能与引用类型约束同时设置。接口约束给一个泛型方法或泛型类调用时候必须实现接口中的方法。调用实际类型必须是现接口。可以和基类约束共存。创建一个接口定义一个接口方法。不可行原因加上生成实现接口加上不报错还有结果输出基类约束在前接口在后调用时实现接口中方法就可以。实现接口后调用就没问题。做接口实现后再创建接口类型对象然后调用泛型方法就可以。应用方法实际类型必须实现接口。无参构造约束通过构造时加一个new构造参数实例的方式。可以通过泛型方法通过new实例创建一个实例。提供不确定类型创建实例通过无参构造函数来做。根据谁创建谁的实例同时出现基类约束接口约束无参构造 先基类后接口new 永远排最后。泛型类new后面加T--无参构造约束接口约束指定的实际类型必须要实现接口。不然会违反接口约束。基类约束必须在第一个无参构造函数函数特殊。如果要写new必须在前where T : [基类约束], [接口1, 接口2, ...], new()值类型约束只能限定值类型引用类型约束只能限定引用类型基类约束只针对基类本身或派生类可以作为实际类型接口约束限定使用实际类型必须实现接口方法无参构造函数限定可以通过无参构造方式来进行实例化。不能和引用约束共用不能和值约束共用。5个约束理解清楚就好。通过反射方式可以直接获取构造函数构造函数无法通过代码直接调用。反射程序集文件类型名可以生成动态程序集。程序集文件类型名 动态程序集的 “文件名 / 扩展名标识通过反射获取程序集名称的细节。定义的方法和细节都可以看。通过类型名了解所有细节。程序可以访问、检测和修改它本身状态或行为的一种能力。之前通过new实例方式创建。可以动态创建实例可以获取某一类型表中对象的类型信息。元数据类中成员里面成员和成员信息指的是里面成员。通过反射获取类成员信息和成员数据。不需要知道源代码只知道类型或类型完整字符串名称就可以有反射了解成员的内部信息和成员数据。获取到类中详细细节的过程。几个类记忆一下。获取数据类型程序集内部信息或某一类型的成员通过类型快速对对象实例化--几个类常用是什么做什么几个类--记忆常用的公有属性和公有成员通过反射来获取私有的一般不推荐。反射只碰公开对外的私有属于内部隐私别强行用反射硬抠既不规范、又不稳定、还不安全。记忆反射用途。通过dll文件动态加载起来。反射用途记忆。通过反射和泛型封装类库。结合泛型和反射封装数据访问通用底层信息基本对象增删改查把基本逻辑对象封装起来。基本和通用方法逻辑结合泛型和反射通用封装使用中代入具体类型就可以调用封装方法去实现增删改查操作。不至于每个类把具体逻辑都写一次。学会用反射去获取某一类型的基本数据去读写对象属性值在对象里面动态加载程序集动态创建实例。不能直接用引用类型或具体使用某一个具体类型不知道引用哪个用反射动态实例化。动态实例化1 实例化2实例化类获取完整名称通过Type对象创建实例。 完整名称命名空间名类名创建本地、远程对象的方法 --Activator完整名称创建实例直接通过类型的Type对象来创建实例。不直接使用类型和直接使用类型Ce内部还是调用无参构造函数来实例化。创建方法本质就是调用无参构造函数。不能直接使用类型在泛型方法里面不确定类型实例利用动态实例化方式Aer查看类型元数据--通过反射查看属性和字段获取类型中细节1.查看元数据获取属性。属性值通过直接代码获取。属性名通过属性数组获取。属性值获取修改属性值get和set方法取值改值获取公开属性取值和改值私有字段不是公开是实例。获取属性和获取字段单个获取和多个获取一样。方法名不同形式名一样元数据--方法及调用静态方法是类成员不是实例成员。构造函数及调用创建类或对象时候直接调用一个构造函数。反射用方法形式调用。获取方法传入调用方法调用是实际类型Type对象执行调用方法后强转。实例object类型需要强转。通过反射创建构造函数及调用构造函数创建对象。反射与用过new调用结果一样。 本质结果一样过程不一样。动态加载程序集--反射方式平常是调用某一个库或项目里面东西先在当前项目引用给添加进来就能引用另一个项目里面类型或调用里面方法。不直接引用--反射拿到要直接引用项目的dll然后通过程序代码方式动态加载起来然后可以获取里面类型调用里面方法。Debug下dll文件程序集名称和dll文件名一样传字符串名称。加载后可以通过程序集获取里面类型。注释后删除生成dll内部项目 除WPF外生成的是可执行文件。三层架构及其它层丢掉内库项目里面。反射与泛型结合应用不确定类型封装成类型参数T后面直接代入实际类型用特定类型进行处理。区别类型不一样类里面细节不一样所做逻辑相同或类似反映通用型处理。--用反射与泛型结合。经常用这种方式定义一些类型相同逻辑的处理基类包含一些公共逻辑。反射经常与泛型结合做通用应用。反射优缺点总结以前有多少类型写多少次。通过反射把以前写的类型现在就用一份逻辑实现调用用一个逻辑实现相同类型来应用。相同应用类型用第一点代入实际不同类型即可代入不同类型实现目标类无序提前目标代码硬编。绕过直接代码过程效率慢直接代码小的没必要。通过直接代码写通用逻辑操作灵活性和拓展性 多段代码用同一个逻辑用反射。没重用性。维护性问题--调试不直接使用。逻辑性相对于直接代码会模糊内部逻辑比直接代码更复杂合理选择。没有应用于多个类型情况程序灵活性或扩展性有更高需求的时候不建议用反射。希望看到直接源码不是通用性反射逻辑不要用反射。反射用在通用性和拓展性基础上更强调灵活。源代码更直接程序逻辑更加清晰不要用反射。通用性大逻辑性和类型不同更强调灵活性用反射。根据场景和需求更合理应用。--灵活性和扩展性高用反射。缺点直接执行上绕过直接代码没有直接代码好。模糊内部逻辑。原则该用可以用没用就不用。属性读写操作字段操作和属性操作基本一样。方法成员无参方法、有参方法、带参方法。熟悉基本操作使用。不为空调用ToString泛型方法通过反射调用就有麻烦。无参不需要传参传null通过反射方式不怎么常用。一般都是直接写方式。通过反射调用实例方法用null记忆这三点加载外部 DLL反射创建类实例反射调用方法、读写属性委托委托就是方法引用。多线程执行逻辑封装到一个类里就是委托。多个方法链接到一起就是多播委托。Lamaba表达式通过匿名函数引用过来。没返回值用voidnew实例化和赋值委托调用任何一个方法调用都可以通过委托合并为实例化对象。通过一个委托合并进行多个调用多播委托。Lambda表达式匿名函数的另外一种编程形式。一条一条语句。 多条语句块{ }左边参数列表。 右边执行主体。Action委托--封装无返回值方法可以带泛型也可以不带泛型无Action委托封装没有泛型方法。泛型里面可以传入6个参数无返回值。Func委托--封装带返回值方法典型泛型委托。可以没返回值参数可以最多传16个参数。最后有return语句事件事件发生时处理事件运行的逻辑。事件声明先有委托。委托和事件区别委托可以在内部调也可以在外部调事件只能在发布器里面调。调用事件后必须触发。调用事件方法在发布器里提供触发事件的方法 提供触发点在什么时候调用。订阅接收事件。订阅事件处理与接收程序关联起来。事件响应会执行调用器中的方法也就是逻辑处理程序。订阅事件方法首先有一个委托通过委托定义和调用事件提供触发方法订阅事件最后通过操作区触发事件去执行事件达到事件应用效果。添加过后进行刷新刷新过后进行调用。事件触发订阅 触发点。特性程序运行时传递元素行为信息的标签。 类中属性、字段都可以用。运行时运动反射技术利用编程方式反馈。自定义特性主讲。类或类中成员。--特性属性第二条。属性名与字段名不一致加一个真实的列名来表示。 实体类属性和列名表示。语法特性名真实类名某一个方法想放弃用新的方法用Obsolete自定义特性有简单需求。直接定义属性一个构造函数构建自定义特性不用写Attritbute通过反射方式去访问。有特性加注释就是注释文本没加注释就是属性文本。LinqLinq介绍与查询语法1.数据源。 2.创建查询。 3.执行查询遍历时候才会执行。Linq查询子句详解一种类型应用成另一种类型投影。分组输出及分组统计用group by数字集合去重方便。多线程基础进程操作系统的资源分配最小运行单元。多线程有点识记。按钮控制逻辑都有主线程创建。回调要执行的任务。线程池执行任务排进去就行。Task--多任务多线程开启子线程另一种方式简单好用--更简洁更简洁T带返回值做进度过程用子线程。开启子任务和子线程的三种方式1-3阻塞卡界面。这种就是卡界面最好别用。等待任何一个任务完成--不会阻塞用户界面体验更好--WhenAny 等待任何一个任务完成不会阻塞。这个不会卡加await等待执行完成后再走。 配对async使用回调就是委托。一个可等一个异步执行。异步调用异步方法通过异步调用不会卡住当前线程程序会 “同时干别的事”等结果回来了再继续执行。使用异步方法配对async标记一个方法是异步方法在异步方法内才能调用和awint是异步调用关键字。执行异步的不一定是子线程很可能是主线程。异步方法内才能进行异步调用。子任务完成后会销毁。界面控件WPF/Winform文本框多层控件一定要加。异步跳过不中断。begin立刻返回。多线程很复杂多任务会做。主线程执行执行完后会卡子线程不会。多线程里面不要把耗时放在主线程。 多线程异常会超范围。创建控件的线程就是主线程委托通过主线程执行。放在for不放在Invoke里主要是等于消耗时间。