字段
1.什么是字段
· 字段是一种表示与对象或类型(类与结构体)关联的变量
· 字段是类型的成员,旧称“成员变量”
· 与对象关联的字段称为“实例字段”
· 与类型关联的字段称为“静态字段”,由static修饰
2.字段的声明
· 尽管字段声明带有分号,但它不是语句
· 字段的名字一定是名词
3.字段的初始值
· 无显式初始化时,字段获得其类型的默认值,所以字段永远不会“未被初始化”
· 实例字段初始化的时机——对象创建时
· 静态字段初始化的时机——类型被加载(load)时
4.只读字段
· 实例只读字段
· 静态只读字段
属性
1.什么是属性
· 属性(property)是一种用于访问对象或类型的特征的成员,特征反映了状态
· 属性是字段的自然扩展
· 属性由Get/Set方法对进化而来
2.属性的声明
· 完整声明——后台成员变量与访问器
· 简略声明——只有访问器
· 动态计算值的属性
· 注意实例属性和静态属性
· 属性的名字一定是名词
· 只读属性——只有getter没有setter
3.属性与字段的关系
· 一般情况下,它们都用于表示实体(对象或类型)的状态
· 属性大多数情况下是字段的包装器(wrapper)
· 建议永远使用属性(而不是字段)来暴露数据,即字段永远是private或protected的
委托
1.什么是委托
· 委托(delegate)是函数指针的“升级版”
· 一切皆地址
-变量(数据)是以某个地址为起点的一段内存中所存储的值
-函数(算法)是以某个地址为起点的一段内存中所存储的一组机器语言指令
· 直接调用与间接调用
-直接调用:通过函数名来调用函数,CPU通过函数名直接获得函数所在地址并开始执行->返回
-间接调用:通过函数指针来调用函数,CPU通过读取函数指针存储的值获得函数所在地址并开始执行->返回
2.委托的声明
· 委托是一种类(class),类是数据类型所以委托也是一种数据类型
· 委托的声明方式与一般的类不同
· 注意声明委托的位置
-避免写错地方导致声明成嵌套类型
· 委托与所封装的方法必需“类型兼容”
-返回值的数据类型一致
-参数列表在个数和数据类型上一致(参数名无需一样)
3.委托的一般使用
· 实例:把方法当作参数传给另一个方法
-正确使用1:模板方法,“借用”指定的外部方法来产生结果
-正确使用2:回调(callback)方法,调用指定的外部方法
· 注意:滥用会导致严重后果
-缺点1:是一种方法级别的紧耦合
-缺点2:降低可读性
-缺点3:把委托回调,异步调用和多线程纠缠在一起,会导致代码难以阅读和维护
-缺点4:委托使用不当可能造成内存泄漏和程序性能下降
事件
1.什么是事件
· 使对象或类具备通知能力的成员
· 用于对象或类间动作协调与信息传递(消息推送)
2.事件的应用
· 派生(继承)与扩展(extends)
· 事件模型的五个部分:
-事件的拥有者(event source,对象)
-事件成员(event,成员)
-事件的响应者(event subscribe,对象)
-事件处理器(event handler,成员)——本质上是一个回调方法
-事件订阅——把事件处理器与事件关联在一起,本质上是一种以委托类型为基础的“约定”
3.注意
` 事件处理器是方法成员
· 挂接事件处理器的时候,可以使用委托实例,也可以直接使用方法名,这是一个语法糖
· 事件处理器对事件的订阅不是随意的,匹配与否由声明事件时所使用的委托类型来检测
· 事件可以同步调用也可以异步调用
4.事件的声明
· 完整声明
· 简略声明(字段式声明)
· 事件的本质是委托字段的一个包装器
-对委托字段的访问起到限制作用
-封装的重要目的是隐藏
-事件对外界隐藏了委托实例的大部分功能,仅暴露添加/移除事件处理器的功能
-添加/移除事件处理器的时候可以直接使用方法名
事件与委托的关系
1.事件真的是“以特殊方式声明的委托字段/实例”吗?
· 不是。只是声明时“看起来像”
· 事件声明时使用了委托类型,简化声明造成事件看上去像一个委托的字段,而event关键字更像一个修饰符——这是错觉来源之一
· 订阅事件时的+=操作符后面可以时一个委托实例,这与委托实力的赋值方法语法相同,这也让事件看起来像一个委托字段——这是错觉的又一来源
· 重申:事件的本质时加装在委托字段上的一个“蒙板”,是个起隐蔽作用的包装器。这个用于阻挡非法操作的蒙板绝不是委托字段本身。
2.为什么要使用委托类型来声明事件?
· 站在source的角度来看,是为了表明source能对外传递哪些信息
· 站在subscribe的角度来看,它是一种约定,是为了能约束能够使用什么样签名的方法来处理事件
· 委托类型的实例将用于存储事件处理器
3.对比事件与属性
· 属性不是字段——很多时候属性是字段的包装器,这个包装器用来保护字段不被滥用
· 事件不是委托字段——它是委托字段的包装器,这个包装器用来保护委托字段不被滥用
· 包装器永远不可能是被包装的东西
接口和抽象类
1.什么是接口和抽象类
· 接口和抽象类都是“软件工程的产物”
· 具体类->抽象类->接口:越来越抽象,内部实现的东西越来越少
· 抽象类是未完全实现逻辑的类(可以有字段和非public成员,它们代表了具体逻辑)
· 抽象类为复用而生:专门作为基类来使用,也具有解耦功能
· 封装确定的,开放不确定的,推迟到合适的子类中去实现
· 接口是完全未实现逻辑的“类”(纯虚类:只有函数成员;成员全部public)
· 接口为解耦而生:“高内聚,低耦合”,方便单元测试
· 接口是一个“协约”(有分工必有协作,有协作必有协约)
· 它们都不能实例化,只能用来声明变量,引用具体类的实例
2.接口与单元测试
· 接口的产生:自底向上(重构),自顶向下(设计)
· C#中接口的实现(隐式,显式,多接口)
· 依赖反转(依赖倒置)
反射与依赖注入
· 反射:以不变应万变(更松的耦合)
· 反射与接口的结合
· 反射与特性的结合
· 依赖注入(Class Library与Assembly)
泛型
· 为什么需要泛型:避免成员膨胀或类型膨胀
· 正交性:泛型类型(类/接口/委托/……)、泛型成员(属性/方法/字段/……)
· 类型方法的参数推断(与lambda表达式结合)
· 泛型与委托、lambda表达式
Partial类
· 减少类的派生
· partial类与Entity Framework
· partial类与Windows Forms,WPF,ASP.NET Core
委托,lambda表达式,LINQ
1.委托
· 内置两类委托,且可自动推断类型
-Action:包裹没有返回值的方法
-Func:包裹有返回值的方法
· +=符号可实现多播委托
2.lambda表达式
· lambda表达式两种应用
-匿名方法
-Inline方法
· lambda表达式可直接赋值给委托
· lambda表达式可作为实参传入方法
3.LINQ(.NET Language Integrated Query)
· Entity Framwork
· Select方法可接收lambda表达式作为参数,用于选取表中字段
· Where方法也可接收lambda表达式作为参数(需返回bool类型值),用于根据指定条件进行筛选
· All方法同上,用于判断是否所有值都符合条件
· 更多可查看C# LINQ methods