- 本博客所总结书籍为《CLR via C#(第4版)》清华大学出版社,2021年11月第11次印刷(如果是旧版书籍或者pdf可能会出现书页对不上的情况)
- 你可以理解为本博客为该书的精简子集,给正在学习中的人提供一个“glance”,以及对于部分专业术语或知识点给出解释/博客链接。
- 【本博客有如下定义“Px x”,第一个代表书中的页数,第二个代表大致内容从本页第几段开始。(如果有last x代表倒数第几段,last代表最后一段)】
- 电子书可以在博客首页的文档-资源归档中找到,或者点击:传送门自行查找。如有能力请支持正版。(很推荐放在竖屏上阅读本电子书,这多是一件美事)
- 欢迎加群学习交流:637959304 进群密码:(CSGO的拆包密码)
目录
- 第十五章 枚举类型和位标志
- 枚举类型
- 位标志
- 向枚举类型添加方法
- 第十六章 数组
- 初始化数组元素
- 数组转型
- 所有数组都隐式派生自System.Array
- 所有数组都隐式实现IEnumberable,ICollection和IList
- 数组的传递和返回
- 创建下限非零的数组
- 数组的内部工作原理
- 固定大小的数组
第十五章 枚举类型和位标志
枚举类型
- 枚举类型(enumerated type)定义了一组“符号名称/值”配对。它从System.Enum直接派生,枚举类型是值类型,但枚举方法不能定义任何方法、属性或事件。(P320 2)C#编译器将枚举类型视为基元类型。所以可用许多熟悉的操作符(==,!=,<,>,<=,>=, ,-,^,&,|,~, 和–)来操纵枚举类型的实例。(P321 4)
- 枚举类型的好处有:1、让程序更容易编写、阅读和维护。 2、枚举类型是强类型的。
- 每个枚举类型都有一个基础类型,它可以是 byte,sbyte,short,ushort,int(最常用,也是C#默认选择的),uint,long或ulong。
//示例
internal enum Color : byte
{
White,Red,Blue,Orange
}
- ToString方法,把值映射为字符串Color c = Color.Red;c.ToSting(“G”));//结果:”Red”(常规格式 Format方法,格式化枚举类型的值:public static string Format(Type enumType,object value,String format);(P322 1)
- Getvalue,GetEnumValues获得每个元素并返回数组。(P322 3)
- GetName,GetNames,GetEnumName,GetEnumNames(P323 2)
//返回数值的字符串表示
public static string GetName (Type enumType,0bject value) ;//system.Enum中定义
public string GetEnumName (object value) ;//System.Type 中定义
//返回一个 string 数组,枚举中每个符号都对应一个string
public static string[] GetNames (Type enumType );//system.Enum 中定义
public string[] GetEnumNames ( ) ;//system.Type中定义
- 符号转换为枚举类型的实例:用Enum提供的静态Parse和TryParse方法(P323 3)
//定义
public static object Parse(Type enumType,string value) ;
public static object Parse(Type enumType,string value,Boolean ignoreCase);
public static Boolean TryParse<TEnum>(String value,out TEnum result) where TEnum: struct;public static Boolean TryParse<TEnum>(String value,Boolean ignoreCase,out TEnum result)where TEnum : struct;
//示例
//因为Orange定义为4, 'c'被初始化为4
color c = (color)Enum.Parse(typeof(Color),"orange",true) ;
//因为没有定义Brown,所以抛出ArgumentException异常
color c = (color)Enum.Parse(typeof (Color),"Brown",false) ;
//创建值为1的color枚举类型实例
Enum.TryParse<Color> ("1", false, out c);
//创建值为23的color枚举类型实例
Enum.TryParse<Color> ("23", false, out c) ;
- 判断枚举对于数值是否合法:IsDefined,IsEnumDefined
//定义
public static Boolean IsDefined(Type enumType,0bject value);//system.Enum中定义public Boolean IsEnumDefined (0bject value) ;//System . Type中定义
//示例
//显示"True",因为Color将Red定义为1
Console.writeLine (Enum.IsDefined (typeof (Color), 1) );
//显示"True",因为color将white定义为0
Console.writeLine(Enum.IsDefined (typeof(Color),"white" ) ) ;
//显示"False",因为检查要区分大小写
Console.writeLine(Enum.IsDefined(typeof(color),"white") ) ;
//显示"False",因为color没有和值10对应的符号
Console.writeine (Enum. IsDefined (typeof(Color),10) ) ;
位标志
- 调用System.IO.File类型的GetAttributes方法,会返回FileAttributes类型的一个实例。FileAttributes类型是基本类型为Int32的枚举类型,其中每一位都反映了文件的一个特性(attribute)。FileAttributes类型在FCL 中的定义如下:(P324 last)
- 为文件设置特性:(P325 1)
//设置只读和隐藏特性
File.SetAttributes(file,FileAttributes.ReadOnly l FileAttributes.Hidden);
向枚举类型添加方法
- 使用扩展方法功能,代码示例(P328 2)
第十六章 数组
- CLR支持一维、多维和交错数组(数组构成的数组)。所有数组类型都隐式地从System.Array抽象类派生,后者又派生自System.Object。这意味着数组始终是引用类型,是在托管堆上分配的。在应用程序的变量或字段中,包含的是对数组的引用,而不是包含数组本身的元素。(P329 1)
- (不明白C#为什么不像C 那些创建数组的看这里)第一行代码声明myIntegers变量,它能指向包含Int32值的一维数组。myIntegers 刚开始被设为 null,因为当时还没有分配数组。第二行代码分配了含有100个Int32值的数组,所有Int32都被初始化为0。由于数组是引用类型,所以会在托管堆上分配容纳100个未装箱Int32所需的内存块。实际上,除了数组元素,数组对象占据的内存块还包含一个类型对象指针、一个同步块索引和一些额外的成员。该数组的内存块地址被返回并保存到myIntegers变量中。(P329 2)
//一维数组
int []myIntegers;//声明一个数组引用
myIntegers = new int [100];//创建含有100个Int32的数组
//创建一个二维数组
Double [ , ]myDoubles = new Double [10,20] ;
//创建一个三维数组,由string引用构成
String [,,]myStrings = new string [ 5,3,10];
//创建交错数组
//创建由多个Point数组构成的一维数组
Point[][]myPolygons = new Point [3] [ ];
//myPolygons [0]引用一个含有10个Point实例的数组
myPoiygons [0] = new Point [10 ] ;
//myPolygons [1]引用一个含有20个Point实例的数组
myPoiygons[1] = new Point [20 ] ;
//myPolygons[2]引用一个含有30个Point实例的数组
myPoiygons [2]= new Point [ 30 ];
//显示第一个多边形中的Point
for(int x= 0 ; x < myPolygons [0 ]. Length; x )
console.writeLine (myPolygons [0][x])
初始化数组元素
- 打括号中的以逗号分隔的数据项称为数组初始化器(array initializer)
string [ ] names = new String[] { "Aidan" ,"Grant" };
//也可以利用C#隐式类型的局部变量功能
var [ ] names = new String[] { "Aidan" ,"Grant" };
//也可以利用C#的隐式类型的局部变量和数组,必须保持初始化的值属于同一类型
var names = new[] { "Aidan" ,"Grant" };
//也可以简化成,但var不行
string [ ] names = { "Aidan" ,"Grant" };
数组转型
- 转型操作:
//创建二维Filestream数组
Filestrean [ , ]fs2dim = new Filestream [ 5,10] ;
//隐式转型为二维Object数组
Object[ , ]o2dim = fs2dim;
//二维数组不能转型为一维数组,编译器报错:
//error CS0030:无法将类型"object[*,*]"转换为"System.IO.stream [ ]"
stream []sldim =( stream [ ] ) o2dim;
//显式转型为二维Stream数组
stream[ , ]s2dim =(stream [ , ]) o2dim;
//显式转型为二维String数组
//能通过编译,但在运行时抛出InvalidcastException异常
string[,] st2cim = (string[.]) o2dim;
//创建一维Int32数组(元素是值类型)
Int32[]ildim = new Int32[5];
//不能将值类型的数组转型为其他任何类型编译器报错:
//error CS0030:无法将类型"int[]“转换为"object [ ]"
object[] oldim = (Object []) ildim;
//创建一个新数组,使用Array.copy将源数组中的每个元素1!转型为目标数组中的元素类型,并把它们复制过去。
//下面的代码创建元素为引用类型的数组,
//每个元素都是对已装箱Int32的引用
object [] ob1dim = new Object [ i1dim.Length] ;Array.copy (ildim,obldim, i1dim. Length) ;
- Array.Copy 的作用不仅仅是将元素从一个数组复制到另一个。Copy方法还能正确处理内存的重叠区域,就像C的memmove函数一样。有C的memcpy函数反而不能正确处理重叠的内存区域。Copy方法还能在复制每个数组元素时进行必要的类型转换,具体如下所述:(P334 1) 1、将值类型的元素装箱为引用类型的元素,比如将一个Int32[]复制到一个ObjectI]中。 2、将引用类型的元素拆箱为值类型的元素,比如将一个Object[]复制到一个Int32[I中。 3、加宽CLR基元值类型,比如将一个Int32[]的元素复制到一个Double[]中。 4、在两个数组之间复制时,如果仅从数组类型证明不了两者的兼容性,比如从 Object[转型为IFormattable[],就根据需要对元素进行向下类型转换。如果 Object[]中的每个对象都实现了IFormattable,Copy方法就能成功执行。
- System.Buffer的BlockCopy支持同类型数组的简单复制。 System.Array的ConstrainedCopy支持复制失败时的抛出异常操作。(P335 Tip)
所有数组都隐式派生自System.Array
- System.Array定义了许多有用的实例方法和属性,比如Clone,CopyTo,GetLength,GetLongLength,GetLowerBound,GetUpperBound,Length,Rank 等。(P335 last)
- System.Array类型还公开了很多有用的、用于数组处理的静态方法。这些方法均获取一个数组引用作为参数。一些有用的静态方法包括:AsReadOnly,BinarySearch,Clear,ConstrainedCopy,ConvertAll,Copy,Exists,Find,FindAll,FindIndex,FindLast,FindLastIndex,ForEach,IndexOf,LastIndexOf,Resize,Reverse,Sort和TrueForAll。(P336 2)
所有数组都隐式实现IEnumberable,ICollection和IList
- 值类型和基类型实现的接口。(P336 3)
数组的传递和返回
- Array.Copy:浅拷贝(对引用类型直接传递回引用对象)(P337 4)
创建下限非零的数组
- 不推荐使用捏。(P338 2)
数组的内部工作原理
- CLR内部支持两种不同的额数组:1、下限为0的一维数组。这些数组有时称为SZ(single-dimensional, zero-based,一维О基)数组或向量(vector)。2、下限未知的一维或多维数组。
- 不安全(unsafe)访问:允许直接内存访问。(P341 2) 1、允许访问堆上的托管数组对象中的元素 2、允许访问非托管堆上的数组中的元素 3、线程栈上的数组中的元素(P342 last)
固定大小的数组
- 通常,由于数组是引用类型,所以结构中定义的数组字段实际只是指向数组的指针或引用;数组本身在结构的内存的外部。不过,也可直接将数组嵌入结构。在结构中嵌入数组需满足以下几个条件: 1、类型必须是结构(值类型);不能再类(引用类型)中嵌入数组。 2、字段或其定义结构必须用unsafe关键字标记。 3、数组字段必须用fixed关键字标记。 4、数组必须是一维0基数组。 5、数组的元素类型必须是以下类型之一:Boolean,Char,SByte,Byte,Int16,Int32,Ulnt16,UInt32,Int64,UInt64,Single或 Double。