这是本人阅读Wrox的《C#高级编程》第4版的笔记(更新中)。作为初学者,帖出本文如果说给大家参考实在是班门弄斧,所以纯是为了留下自己在技术之路上不起眼的脚印罢了
C#高级编程(第4版)学习笔记
第一章 .NET体系结构
a) 公共语言运行库(CLR)
i. CLR的控制下运行的代码叫托管代码(managed code)。
ii. .NET中编译分为两个阶段:
1. 把源代码编译为Microsoft中间语言(MSIL),MSIL是数字代码而不是文本代码。
2. CLR把MSIL 编译为平台专用代码。
iii. 托管代码的优点
1. 平台无关性(还未实现,目前.NET只能运行在Windows平台上)
2. 提高性能:MSIL是即时编译(JIT编译)的,只编译它调用的那部分代码,得到的内部可执行代码就存储起来,直到应用程序退出,这样下次运行这部分代码就不用重新编译了。(任何应用程序的大部分代码实际上并不是在每次运行过程中都执行,且JIT在编译时可以基于特定处理器优化代码,传统编译器只能独立于处理器优化代码)。
iv. 语言的互操作性:C#,Visual Basic 2005, Visual C++ 2005, Visual J# 2005, 脚本语言(Javascript.NET),COM和COM+都能编译成MSIL通过.NET进行交互操作。
b) 中间语言(MSIL)
i. MSIL采用传统的面向对象编程,带有类的单一继承性。
ii. 引入接口:提供一个契约,实现给定接口的类必须提供该接口指定的方法和属性的实现方式。
iii. 语言互操作性:一种语言编写的类应该能直接与用另一种语言编写的类通信:
1. 用一种语言编写的类应能继承用另一种语言编写的类。
2. 一个类应能包含另一个类的实例,不管它们是使用什么语言编写的。
3. 一个对象应能直接调用其他语言编写的另一个对象的方法。
4. 对象(或对象的引用)应能在方法之间传递。
5. 应能调试不同语言编写的源代码。
iv. 值类型和引用类型有明显区别
1. 值类型变量直接保存其数据。
2. 引用类型变量保存其地址,数据可在该地址中找到。
v. 中间语言基于强数据类型,所有的变量都清晰地标记为属于某个特定数据类型。
1. 通用数据类型(CTS):CTS定义了可以在中间语言中使用的预定义数据类型。所有语言的预定义类型编译成MSIL里都映射为一个MSIL内置类型。
2. 公共语言规范(CLS):CLS是一个最低标准集,所有面向.NET的编译器都必须支持它。CLS兼容特性只适用于public和protected类成员和公共类。在类的私有实现方式中可以编写非CLS代码,因为其他程序集中的代码不能访问这部分代码。
3. 垃圾回收:Windows平台使用两种技术释放内存:1)手工使应用程序代码完成这些工作,2)让对象维护引用计数,而.NET使用垃圾收集器:所有动态请求的内在都分配到堆上,当.NET检测到给定进程的托管堆已满,就调用垃圾收集器(代码中可以调用垃圾收集器)
vi. 安全性:Windows提供基于角色的安全性,.NET提供基于代码的安全性。
vii. 应用程序域:多个应用程序进行在同一进程中,保证它们之间通信的性能和安全性。
viii. 通过异常方法处理错误:.NET提供一组类表示异常。
ix. 特性的使用:
c) 程序集
i. 程序集:编译好的、面向.NET Framework的代码逻辑单元。它可以存储在多个文件中(其中会有一个包含入口点的主文件)。程序集包含的元数据描述了对应代码中定义的类型和方法。元数据和可执行指令放在一起防止信息不同步。
ii. 私有程序集:私有程序集一般附带在某些软件上,且只能用于该软件中。通常以可执行文件或多库的方式提供应用程序,这些库包含的代码只能用于该应用程序(安全性好)。它的安装过程简单,只需 相应的文件放在文件系统的对应文件夹中即可(用需要注册表项),这称为“0(xcopy)影响安装”。
iii. 共享程序集:它是其他应用程序可以使用的公共库。采用全局程序集高速缓存(GAC)来解决名称冲突和被同一程序集的不同版本覆盖。共享程序集应根据私有密钥加密法指定一个名称,该名称叫做强名(strong name),并保证其唯一性。
iv. 反射:编程访问程序集中定义的所有类型和这些类型成员的细节的元数据称为反射。
d) .NET Framework类
i. .NET基类是一个内容丰富的托管代码类集合,它可以完成大多数通过Windows API完成的任务。这些类派生自与中间语言相同的对象模型,也基于单一继承性。
e) 命名空间
i. 命名空间是.NET避免类名冲突的一种方式。
ii. 它是数据类型的一种组合方式,其中所有数据类型的名称都会自动加上该命名空间的名字作为其前缀。
iii. 命名空间可以嵌套
iv. 如果没有显式提供命名空间,类型就添加到一个没有名称的全局命名空间中。
f) 用C#创建.NET应用程序
i. 创建ASP.NET应用程序
1. ASP.NET页面是结构化的(ASP不是)。也就是说 ,每个页面都是一个继承了.NET类System.Web.UI.Page的类,可以重写在Page对象的生存期中调用的一系统方法。
2. ASP.NET的后台编码功能允许进一步采用结构化的方式,将页面的服务器端单独放在一个类中,把该类编译为DLL,与HTML部分下面的一个目录中。
3. WEB服务器在编译后高速缓存ASP.NET页面,这表示以后对ASP.NET页面的请求就比ASP页面的执行速度快很多。
4. 提供Web窗体,例如:在使用C#创建Web窗体时,就是创建一个继承于Page基类的C#类,以及把这个类看作是后台编码的ASP.NET页面。
5. 提供Web服务器控件:它们是ASP.NET命名空间中的XML标记,当请求页面时,Web浏览器会动态地把它们转换为HTML和客户端脚本。
6. XML Web服务:为面向Web的服务而设计,远程计算机彼此提供可以分析和重新格式化的动态信息,最后显示给用户。优点:把HTTP用作传输信息的媒介;XML数据格式是自我描述的、非专用的,独立于平台的。
ii. 创建Windows窗体程序
iii. 创建Windows控件
iv. 创建Windows服务:当希望程序连续运行,响应事件,但没有用户的明确启动操作时就应该使用Windows服务。
第二章 C#基础
a) C#所有的工作都依赖于.NET基类。
b) 第一个C#程序
i. Main()只能返回void和int
ii. C#的方法定义:[modifiers] return_type MethodName([parameters]){ //Method body}
iii. Static表示不能在类的特定实例上执行,因此不必先实例化类再调用。
c) 变量
i. C#的变量声明:datatype identifier; C#不区分对象和简单类型,即使要把变量指向一个对象,也不需要Set关键字,任何数据类型的声明语法都是相同的。
ii. 变量初始化
1. 变量是类或结构中的字段,如果没有显式的初始化,在默认状态下其值为0.
2. 方法的局部变量必须在代码中显式初始化,然后才能调用,否则会产生编译错误。同样的规则也适用于引用类型。Something objSomething;只是为Something对象创建一个引用而没有指向任何对象,objSomething=new Something();把该引用指向存储在堆上的一个对象。
iii. 某些情况下,可以区分名称相同(完全限定名不同)、作用域相同的两个标识符。因为编译器把声明为类型级的变量看作字段,把在方法中声明的变量看作局部变量。(要在方法中调用该字段用语法object.fieldname,如果访问实例字段要用this关键字)。
iv. 常量:C#中只能把局部变量和字段声明为常量
1. 常量必须在声明时初始化,其值指定后就不能再修改。
2. 不能用一个变量中提取的值来初始化常量(只读字段例外)。
3. 常量总是静态的,但不用也不允许显示地用static标识。
d) 预定义数据类型
i. 值类型和引用类型
1. 值类型直接存储其值(在堆栈中)
2. 引用类型存储对值的引用(在托管堆上)
3. 如果变量是一个引用,就可以把其值设置为null,表示它不引用任何对象。
4. 变量是值还是引用只取决于其数据类型。一般情况下,基本类型是值类型,而自定义类型为引用类型(如果要自定义值类型,就应把它声明为一个结构)。
ii. 通用数据类型(CTS)
1. C#中的基本预定义类型并没有内置于语言中,而是内置于.NET Framework中,这表示在语法上,可以把所有的基本数据类型看作是支持某些方法的类。
iii. 预定义值类型
1. 整型:所有整型变量都能赋予10进制和16进制的值,默认为int
|
名称 |
CTS类型 |
说明 |
|
Sbyte |
System.SByte |
8位有符号整数 |
|
Short |
System.Int16 |
16位有符号整数 |
|
Int |
System.Int32 |
32位有符号整数 |
|
Long |
System.Int64 |
64位有符号整数 |
|
Byte |
System.Byte |
8位无符号整数 |
|
Ushort |
System.Uint16 |
16位无符号整数 |
|
Uint |
System.Unit32 |
32位无符号整数 |
|
Ulong |
System.Uint64 |
64位无符号整数 |
2. 浮点类型:默认为double
|
名称 |
CTS类型 |
说明 |
位数 |
|
float |
System.Single |
32位单精度浮点数 |
7 |
|
double |
System.Double |
64位双精度浮点数 |
15 |
3. decimal类型:专用于财务计算,不是基本数据类型,计算时有性能损失,例:decimal d=12.30M
|
名称 |
CTS类型 |
说明 |
位数 |
|
decimal |
System.Decimal |
128位高精度十进制数表示法 |
28 |
4. bool类型:不能和整数值相互转换
|
名称 |
CTS类型 |
值 |
|
Bool |
System.Boolean |
true or false |
5. 字符类型:其字面量用单引号括起来
|
名称 |
CTS类型 |
值 |
|
char |
System.Char |
表示一个16位Unicode字符 |
转义序列
|
转义序列 |
字符 |
|
/’ |
‘ |
|
/” |
“ |
|
// |
/ |
|
/0 |
空 |
|
/a |
警告 |
|
/b |
退格 |
|
/f |
换页 |
|
/n |
换行 |
|
/r |
回车 |
|
/t |
水平制表符 |
|
/v |
垂直制表符 |
iv. 预定义的引用类型
|
名称 |
CTS类 |
说明 |
|
object |
System.Object |
根类型,CTS中的其他类型都是从它派生而来的 |
|
string |
System.String |
Unicode字符串 |
1. object类型:C#中,object类型是所有类型的父类型。
a) 可以使用object引用绑定任何子类型的对象。
b) object类型执行许多基本的一般用途的方法,包括Equals(),GetHashCode(),GetType()和ToString()。
2. string类型:
a) string类型在命名空间System.String中
b) 它是一个引用类型,因此当把一个字符串变量赋给另一个字符串时,会得到对内存中同一个字符串的两个引用。但是它与引用类型在常见的操作上有一些区别。例如:修改其中一个字符串,就会创建一个新的string对象,而另一个字符串没有改变。
c) 字符串字面量放在双引号中,如果放在单引号中编译器就会把它当作char。
d) 它也可以包括Unicode和16进制数转义序列。
e) 可以在字符串字面量前加上字符@,在这个字符后面的所有字符都看作是其原来的含义,而不会解释为转义字符(换行符也一样)。
e) 流控制
i. 条件语句:
1. if语句:C#中的if语句不能直接测试整数。
2. switch语句:适合于从一组互斥的分支中选择一个执行分支。
a) 不需要使用括号组合case子句中的代码,只要用break语句标记每个case代码的结尾即可。
b) 可以在switch语句中包含一个default子句,如果表达式不等于任何case子句的值就执行default子句的代码。
c) case的值必须是常量表达式,不允许使用变量。
d) 如果激活了块中的一个case子句,后面的case子句就都不会被激活(除非使用goto特别指定或case子句为空),编译器会把没有break的case子句标记为错误。
e) C#中case子句顺序无所谓,但是两个case不能相同,值相同的不同常量也不行。
f) 可以将字符串用作测试变量。、
ii. 循环:foreach中不能改变集合中各项的值(for循环可以)
iii. 跳转语句
1. goto语句(不推荐使用)
a) 可以直接跳转到程序中用标签指定的别一行
b) 不能跳到for循环这样的代码块中,不能跳出类的范围,不能退出try…catch块后面的finally块
2. break语句:跳出最内层循环或在case子句中使用
3. continue语句:必须在循环中使用,但它只从循环的当前迭代中退出,然后在循环的下一次迭代开始重新执行,而不是退出循环。
4. return语句:退出类的方法。
f) 枚举:枚举是用户定义的整数类型,在声明一个枚举时,要指定该枚举可以包含的一组可接受的实例值, 可以给值指定易于记忆的名称。
i. 枚举的优势:
1. 枚举可以使代码更易于维护,有助于确保给变量指定合法的值。
2. 使代码更清晰,允许用描述性的名称表示整数值,而不是用含义模糊的数来表示。
3. 使代码更易于键入(支持IntelliSense)
ii. 枚举在后台会实例化为派生于基类System.Enum的结构,这表示可以对它们调用方法。
iii. 从字符串中获取枚举值:TimeOfDay time2=(TimeOfDay)Enum.Parse(typeof(TimeOfDay),”afternoon”,true); typeof后括号中是枚举类名,“”中是要转换的字符串,后而是是否要忽略大小写。最后,Enum.Parse()返回的是一个对象引用,需要把它显式转换为需要的枚举类型(拆箱操作)。
g) 数组
i. 数组都是引用类型,遵循引用的语义。
ii. 初始化特定大小的数组,用new关键字。int[] integers=new int[32];
iii. ArrayName.Length表示数组包含多少个元素。
h) 命名空间
i. 命名空间是一种逻辑组合,它提花了一种组织相关类和其他类型的方式。
ii. 命名空间中可以嵌套其他命名空间。
iii. 不允许在另一个嵌套的使命空间中声明多部分的命名空间。
iv. 命名空间与程序集无关。
v. using语句并没有在文件之间建立真正的物理链接(和#include不同)
vi. 命名空间的别名 using 别名=NamespaceName;别名的修饰符是:: 例如:别名::类名 GetNamespace()返回命名空间名,该方法调用每个类都有的GetType()方法,等于GetType().Namespace;
i) Main()方法
i. Main()方法必须是静态方法,返回类型只能是int或void。
ii. 一般不允许一个程序中有多个Main()方法,但是可以在控制台中用/main:后面跟Main()方法所属类的命名(包括命名空间)明确告诉编译器把哪个方法作为程序的入口点。
iii. 给Main()方法传送参数:Main()接受字符串数组参数,在启动程序时,可以使用这个数组通过命令行传送过来的选项。
j) 控制台I/O
i. Console.ReadLine():从控制台窗口中取一个输入流(输入回车停止),并返回输入的字符串。
ii. Console.Write():将指定的值写入控制台窗口。
iii. Console.WriteLine():比Cconsole.Write()在最后多一个换行符,它支持格式化。例如:Console.WriteLine(“{0} plus {1} equals {2}”,i,j,i+j); 也可以为值指定宽度{n,w}其中n是参数索引,w是宽度值,还可以添加一个格式字符串,和一个可选的精度值,可以用占位符来代替格式字符串,例如:Console.WriteLine(“{0:#.00},d);
|
字符串 |
说明 |
|
C |
本地货币格式 |
|
D |
十进制格式 |
|
E |
科学计数法 |
|
F |
定点格式,精度说明符设置小数位数 |
|
G |
普通格式,用E或F中较简单的格式 |
|
N |
数字格式,用逗号表示千分符 |
|
P |
百分数格式 |
|
X |
16进制格式 |
k) C#预处理器指令
i. #define和#undef:声名和取消名称符号的存在
ii. #if,#elif,#else,#endif这些指令告诉编译器是否要编译某个代码块。例如:#if DEBUG //do something #endif, #if,#elif支持逻辑运算符。
iii. #warning和#error:分别产生警告和错误
iv. #region和#endregion:用于把一段代码标记为有给定名称的一个块
v. #pragma:抵制或恢复指定的编译警告,可以在类中运行,例如:#pragma warning disable 169 //classbody #pragma warning restore 169
l) C#编程规则
i. 必须用字母或下划线开头,可以包含数字
ii. 不能用C#关键字(除非用@前缀)
iii. 标识符可以包含Unicode字符,用语法/uXXXX来指定,XXXX是Unicode字符的四位16进制代码
iv. C#用法约定:
1. C#中命名变量不使用任何前缀。
2. 变量名要反映变量实例的功能而不是数据类型(控件例外)。
3. 采用Pascal大小写命名形式:单词首字母大写,不使用下划线
4. 类型中所有私有成员字段的名称,传递给方法的参数,都应是camel大小写形式:第一个单词首字母不大写,其他单词首字母大写。
5. 名称的风格应保持一致。
6. 命名空间的名称:Microsoft建议:<CompanyName><TechName>
7. 不要用和关键字冲突的名称(最好也不使用和其他语言关键字相冲突的名称)
8. 属性和方法的使用:如果该对象的外观和操作都像一个变量就应使用属性来表示,如果属性的值可能会出现预料不到的改变,就应改用方法。
9. 多数情况下字段应为私有的。
第三章 对象和类型
a) 类和结构:类和结构都是创建对象的模板,每个对象都包含数据,并提供了处理和访问数据的方法。都用new关键字来实例化。(多数情况下使用类而不使用结构)
i. 类存储在堆(heap)上,用class声明。
ii. 结构存储在堆栈上(所以访问效率比类高),用struct声明。
b) 类成员:类中的数据和函数称为类的成员。别外,类中可以包含嵌套类。public类:在类的内部、外部都能访问。private类,只能在类内部访问。protected类:只能该成员所在类及其派生类访问。
i. 数据成员:数据成员包括了类的数据——字段、常量和事件。
ii. 静态数据:与整个类相关,实例数据:类的每个实例都有它自己的数据副本。对于面向对象语言,类成员总是实例成员,除非用static进行显式声明。
iii. 事件:事件是类的成员,在发生某些行为时,它可以让对象通知调用程序。客户端可以包含“事件处理程序”的代码响应该事件。
c) 函数成员:函数成员提供了操作类中数据的某些功能,包括:方法、属性、构造函数、终结器(finalizer)、运算符以及索引器。
i. 方法:是与某个类相关的函数,包括实例方法,静态方法。
1. C#中不能有全局函数,方法必须与类或结构相关。
2. 方法的声明:[modifiers] return_type MethodName([parameters]) {//Method body}
3. 如果返回空类型,return可以省略。方法中可以包含多个return。
4. 给方法传递参数:
a) 参数可以通过引用或值传递给方法。
b) 引用传递给方法时,被调用的方法得到的就是这个变量,对变量进行的改变在方法退出后仍旧发挥作用。
c) 变量通过值传递给方法时,被调用的方法得到的是变量的一个副本,方法退出后,对变量进行的修改会丢失。
d) 对于复杂的数据类型,按引用传递的效率更高,因为在按值传递时,必须复制大量的数据。
e) 如果没有特殊说明,C#中的参数都按值传递。可以在参数前ref来显式使用引用传递参数(注意,在调用时也要在参数前加ref)。
f) 在方法调用中,对字符串的任何改变都不会影响原来的字符串。
g) 变量被当作参数传递时必须事先初始化(在参数前加out关键字可以不事先初始化,该变量将通过引用传递,从方法中返回。调用时参数前也要加out)。
h) 方法的重载:要重载方法,只需声明同名但参数个数或类型不同的方法即可。
i. 两个方法不能仅在返回类型上有区别。
ii. 两个方法不能仅根据参数是声明为ref还是out来区分。
ii. 属性:是可以在客户机上访问的函数组,基本访问方式与访问类的公共字段类似。C#为属性的访问提供了专门的语法。
1. 它是一个方法或一对方法,在客户机代码看来,它们是一个字段。
2. 声明:public string SomeProperty{get{return “value”;} set{//set the property}}
3. get访问器不带参数,且必须返回属性声明的类型。
4. set访问器不指定任何显式参数,编译器假定它带一个与属性类型相同的参数。
5. 只读与只写属性:
a) 只有get的属性是只读属性。
b) 只有set的属性是只写属性。(不推荐使用,最好用方法代替)
6. 属性的访问修饰符
a) set和get设置为公共的(默认),私有的或受保护的。
b) set和get中必须至少有一个具备属性的访问级别(公共的)。
7. 内联:CLR会在编译时适当的时候自动用内联代码来替代函数调用。
iii. 构造函数:是在实例化对象时自动调用的函数。它们必须与所属的类同名,且不能用返回类型。构造函数用于初始化字段的值。
1. 如果没有显式的构造函数,编译器会在后台创建一个默认的构造函数,它把所有的成员字段初始化为标准的默认值(基本类型为空,数字数据类型为0,布尔类型为false)。
2. 构造函数可以提供任意多的重载,只要它们的签名有明显区别即可。
3. 一般使用this区别成员字段和同名参数,成员字段前用this.。
4. 可以把构造函数定义为private或protected
5. 静态构造函数:class MyClass{static MyClass(){//initialization code}}
a) 使用静态构造函数的原因:类有一些静态字段或属性,需要在第一次使用类之前从外部源中初始化这些静态字段和属性。
b) C#中,静态构造函数通常在第一次调用类的成员之前执行。
c) 静态构造函数不带访问修饰符,也不带参数,只能访问静态成员。
d) 如果静态字段有默认值,它们就在调用静态构造函数之前指定。
e) 可以从其他构造函数中调用构造函数,用this关键字调用参数最匹配的那个构造函数。
f) 可以从基类中调用构造函数,用base关键字。
6. 只读字段:用readonly关键字声明,允许把一个字段设置为常量,但可以执行一些运算以确定它的初始值。
a) 只读字段可以是实例字段,在每个实例中可以有不同的值,其值在实例中生成。
b) 如果只读字段要设置为静态,必须显式声明。
c) 不必在构造函数中给只读字段赋值。
iv. 终结器:类似于构造函数,但是在CLR检测到不再需要某个对象时调用。它们的名称与类相同,但前面多一个“~”。
v. 运算符:允许重载。
vi. 索引器:允许对象以数组或集合的方式进行索引。
d) 结构:
i. 用struct关键字声明,为结构定义函数与为类定义函数完全相同。
ii. 一般存储在堆栈中(当它是一个对象的一部分时就存储在堆中)
iii. 结构不支持继承
iv. 编译器提供一个无参数的构造函数,这是不允许替换的。
v. 使用结构可以指定字段在内存中如何布局。
vi. 结构是值类型
1. 可以写Dimensions point=new Dimensions();但new运算符并不分配堆中的内存,而是调用相应的构造函数,根据传递给它的参数初始化所有的字段。
2. 也可以写Dimensions point;对于结构,变量声明实际上是为整个结构分配堆栈中的空间,所以就可以赋值了。
3. 结构在使用前所有元素都必有初始化。
4. 把一个结构赋给另一个结构,内容就被复制(对于类,只复制引用),因此有性能损失。结构作参数时,用ref强制复制引用可以避免性能损失,但要注意调用的方法可以改变结构的值。
vii. 结构和继承
1. 结构一不能从另一个结构中继承,但结构派生自System.ValueType,而它又派生自System.Object,所以结构可以访问和重写System.Object中的方法。
viii. C#中的结构内禁止使用无参数的构造函数。
e) 部分类:partial关键字允许把类、结构或接口放在多个文件中。
i. 编译时每个部分将合在一起
ii. 如果声明类时使用了下面的关键字,它们将应用于同一个类的所有部分:
1. public
2. private
3. protected
4. internal
5. abstract
6. seald
7. 基类
8. new
9. 一般约束
iii. 在嵌套的类型中,只要partial关键字位于class关键字的前面,就可以嵌套部分类。编译里,会无关属性、XML注释、接口、一般类型的参数属性和成员。
f) 静态类:如果类只包含静态的方法和属性,该类就是静态的。使用static关键字声明。静态类不能实例化。
g) Object类
i. .NET中的所有类都派生于System.Object。
ii. 如果在定义类时没有指定基类,编译器就会自动假定这个类派生于Object。
iii. System.Object方法
|
方法 |
访问修饰符 |
作用 |
|
string ToString() |
public virtual |
返回对象的字符串表示 |
|
int GetHashCode() |
public virtual |
在实现字典(散列表)时使用 |
|
bool Equals(object obj) |
public virtual |
对对象的实例进行相等比较 |
|
bool Equals(object objA,object objB) |
public static |
对对象的实例进行相等比较 |
|
bool ReferenceEquals(object objA,object objB) |
public static |
比较两个引用是否指向同一个对象 |
|
Type GetType() |
public |
返回对象类型的详细信息 |
|
object MemberwiseClone() |
protected |
进行对象的浅表复制 |
|
void Finalize() |
protected virtual |
析构函数的.NET版本 |
iv. Object.ToString()声明为虚类型,如果不在自己定义的类中重写ToString(),该类将只继承System.Object执行方式——显示类的名称。如果希望ToString()返回一个包含类中对象的值信息的字符串,就要重写它。
这篇博客详细介绍了C#高级编程的.NET体系结构,包括公共语言运行库(CLR)、中间语言(MSIL)、语言互操作性、值类型和引用类型等概念。此外,还讲解了C#的基础知识,如变量、数据类型、控制流、数组、命名空间以及预定义数据类型。内容深入浅出,适合C#初学者和想进一步理解.NET框架的开发者阅读。
427





