24.8.14 《CLR via C#》 笔记12

第十五章 枚举类型和位标志

  1. 使用枚举类型而不是硬编码的理由:枚举类型更易编写,阅读和维护;枚举类型是强类型
  2. 枚举类型是值类型,不能定义任何方法,属性或事件,可利用扩展方法向枚举类型添加方法
  3. 枚举类型定义的值是常量值,枚举类型实际是一个结构,其中定义了一组常量字段和一个实例字段(value__)
  4. 枚举类型都有一个基础类型,容纳枚举类型的值(int,byte,short等)
  5. C#编译器将枚举类型视为基元类型,所以可用操作符(==,+,&等),实际作用于value__字段;枚举类型可以显式转换成不同的枚举类型
  6. ToString方法把枚举类型的实例值映射为字符串表示,可选用格式G常规,D十进制,X十六进制;同时支持Format方法格式化
  7. Enum.GetValues和Type.GetEnumValues返回枚举类型符号名称的数组;Enum.Parse和Enum.TryParse将符号转换成枚举类型的实例;Enum.IsDefined判断数值对于某枚举类型是否合法;Enum.ToObject将Byte,Int32等类型的实例转换成枚举类型的实例
  8. 位标志:经常使用枚举类型表示一组可组合的位标志(例如FileAttributes)
    1. 可使用[Flag]特性,调用ToString时,数值可以不再视为单独的符号,而是一组位标志的组合并返回字符串(每个枚举值都被表示为2的幂次,没有直接被定义的数值5(1+4)会返回Flag1,Flag3)
    2. Parse和TryParse可以将以逗号分隔的符号字符串转换成数值
    3. 位标志不能使用IsDefined方法
  9. 不能将方法定义为枚举类型的一部分,但是可以利用扩展方法模拟向枚举类型添加方法

第十六章 数组

  1. 数组从System.Array抽象类派生
  2. 值类型数组元素直接储存在数组内存空间中(局部变量在栈中,类成员在堆上),复制时复制的是全新的数组副本;引用类型数组存储的是指向数组元素的引用,数组元素本身在堆上,复制只会复制引用
  3. CLR不允许将值类型的数组转换为其他任何类型;引用类型数组转型要求数组维数相同,且必须存在从原类型到目标类型的隐式或显式转换
  4. Array.Copy执行浅拷贝;它能正确处理内存的重叠区域(源数组和目标数组的内存区域存在重叠,并且复制方向是从低地址到高地址时,直接复制会导致数据被覆盖)
  5. 所有数组隐式的实现IEnumerable,ICollection和IList
  6. 无论是值类型数组还是引用类型数组,在 C# 中作为参数传递时,传递的都是数组的引用
  7. 可以使用Array.CreateInstance创建下限非零的数组(不提倡使用非0基数组)
  8. 数组内部工作原理:
    1. 长度Length代表数组中的元素数量,Rank代表数组的维度
    2. 访问数组元素时会进行边界检查,unsafe代码可以关闭索引上下限检查
    3. 编译器已优化for循环中对Length的判断,不用缓存一个Length
  9. 使用stackalloc可以在栈上分配数组(分配速度快、自动释放、栈上数据彼此靠近可能提高CPU缓存利用率),但是不能将这种数组传递给大部分FCL方法,且需要打开unsafe开关
  10. 可以将数组嵌入结构(必须是值类型):使用unsafe标记结构,用fixed标记数组字段,必须是0基,元素只能是指定的几种类型之一

第十七章 委托

  1. 委托提供回调函数机制,它保证回调方法是类型安全的(非托管c/c++回调函数只是函数地址,不提供期望的参数个数、参数类型、返回值类型和调用协定等信息)
  2. 委托可以回调静态方法,也可以回调实例方法。将方法绑定到委托时,允许引用类型的协变性(方法能返回从委托的返回类型派生的类型)和逆变性(方法获取的参数可以是委托的参数类型的基类)
  3. 定义委托时,编译器定义一个继承System.MulticastDelegate的完整的类,其构造器接受一个指向目标方法的指针,以及一个可选的 object 类型的参数,用于存储目标对象 (如果目标方法是实例方法)
  4. 委托用_invocationList字段实现委托链,第一个方法添加到委托实例时,该字段被初始化,新建一个委托实例包装这个方法,并加进list中,以后再添加/移除方法,都会合并这个list
  5. 调用委托时,会遍历_invocationList数组,由于是顺序调用,当其中一个委托对象阻塞或者抛出异常,都会影响到后续的所有对象。可以使用GetInvocationList方法显式调用每一个委托
  6. 由于定义的每个委托都会生成新的类,不建议定义太多,建议使用泛型委托Action和Func
  7. C#的为委托语法糖
    1. 不需要构造委托对象,只要提供方法名
    2. 不需要定义回调方法,可以使用Lambda表达式
    3. 局部变量不需要手动包装到类中即可传给回调方法(这使局部变量延长了生命周期)
  8. 使用反射:MethodInfo.CreateDelegate方法在运行时动态地创建一个委托,将该委托绑定到指定的静态方法或实例方法;Delegate.DynamicInvoke方法可以调用委托对象的回调方法,传递一组在运行时确定的参数

第十八章 定制特性

  1. 定制特性是一个类型的实例,从抽象类System.Attribute派生,应用时允许省略Attribute后缀
  2. 特性的构造器参数称为定位参数;还可以添加设置公共字段属性的参数,称为命名参数
  3. 定义自己的特性类,类名Attribute后缀不是必须的;至少要包含一个公共构造器;这个类应该只提供状态信息,包含字段和属性,但是不应提供方法、事件或其它成员
  4. 在特性上可以应用特性System.AttributeUsageAttribute,指明特性的合法应用范围(AttributeTargets),能不能被多次应用于同一个目标(AllowMultiple),是否同时应用于派生类和重写的方法(Inherited)。如果没有指定,则默认可以应用于所有目标、只能应用一次、可继承
  5. 定义特性类的实例构造器、字段和属性时,只能从指定的数据类型中选择,应用特性时必须传递编译时常量表达式,可以理解为特性要被序列化元数据字节流中
  6. 特性本身除了在程序集中生成额外的元数据以外没有其它意义,必须实现一些额外的代码,利用反射检测某些目标上是否存在该特性的实例,然后执行一些代码
    1. IsDefined判断目标是否应用了特性(比较高效)
    2. GetCustomAttributes和GetCustomAttribute将创建指定特性的新实例,然后扫描元数据,用指定的值设置每个实例的字段和属性值,然后返回对特性实例的引用
    3. 若要不创建从Attribute派生的对象:先用ReflectionOnlyLoad加载程序集,然后CustomAttributeData类获取CustomAttributeData集合,每个元素都是目标的一个定制特性
  7. 除了直接检查特性类的值,还可以利用Attribute重写的Equals判断两个特性对象的类型、字段值是否一致,以移除反射的使用;还可以重写Equals和Match提供更丰富的语义
  8. 条件特性类ConditionAttribute:只当定义了指定符号的前提下,编译器才会生成特性信息(例如某些特性只应用于代码分析工具,而不停留在元数据中增大文件大小)

第十九章 可空值类型

  1. 可空值类型Nullable<T>:实现使值类型为null,但仍是值类型(比值类型本身多了一个bool字段)
  2. C#允许使用?声明和初始化:Int32? x=5等价于Nullable<Int32> x=5;支持使用操作符(一元操作符、二元操作符、相等性操作符、关系操作符);还可以重载上述操作符
  3. 空接合操作符??获取两个操作数,假如左边不为null则返回左边,否则返回右边的值,能用于值类型和引用类型,例如string s = Method1() ?? Method2() ?? “Untitled”
  4. ??操作符类似?:操作符,但是某些情况下更易读
  5. 对Nullable<T>装箱时,如果是null则返回null,否则返回T的装箱
  6. 对象拆箱为Nullable<T>时,如果对象是null则返回一个Nullable<T>并将值设为null
  7. 对Nullable<T>使用GetType(),返回T的类型
  8. 通过Nullable<T>调用接口方法,就算接口只实现了以T为参数的方法,也能通过编译
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值