发布时间:2022-12-30 15:00
1 .NET体系结构
1.1 C#与.NET的关系
C#是一种相当新的编程语言,C#的重要性体现在以下两个方面:
它是专门为与Microsoft的.NET Framework一起使用而设计的(.NET Framework是一个功能非常丰富的平台,可开发、部署和执行分布式应用程序)。
它是一种基于现代面向对象设计方法的语言,在设计它时,Micosoft还吸取了其他所有类似语言的经验。
一个很重要的问题要弄明白:C#就其本身而言只是一种语言,尽管它是用于生成面向.NET环境的代码,但它本身不是.NET的一部分。.NET支持的一些特性,C#并不支持。而C#语言支持的另一些特性,,NET却不支持(如运算符重载)。
1.2 公共语言运行库
.NET Framework的核心是其运行库执行环境,称为公共语言运行库(CLR)或.NET运行库。通常将在CLR控制下运行的代码称为托管代码(managed code)。
在CLR执行编写好的源代码(在C#中或其它语言中编写的代码)之前,需要编译他们。在.NET中,编译分为两个阶段:
1)把源代码编译为Microsoft中间语言(IL)。
2)CLR把IL编译为平台专用的代码。
1.3 中间语言
语言互操作性:
1)用一种语言编写的类应能继承用另一种语言编写的类。
2)一个类应能包含另一个类的实例,而不管两个类是使用什么语言编写的。
3)一个对象应能直接调用用其他语言编写的另一个对象的方法。
4)对象(或对象的引用)应能在方法之间传递。
5)在不用的语言之间调用方法时,应能在调试器中交替调试
垃圾回收的一个重要方面是它的不确定性。换言之,不能保证什么时候会调用垃圾回收器:CLR决定需要它时,就可以调用它。但可以重写这个过程,在代码中调用垃圾回收器。
1.4 程序集
程序集(assembly)是包含编译好的,面向.NET Framework的代码的逻辑单元。
注意:可执行代码和库代码使用相同的程序集结构,唯一的区别是可执行的程序集包含一个主程序的入口点,而库程序集不包含。
共享程序集是其他应用可以使用的公共库。因为其他软件可以访问共享程序集,所以需要采取一定的保护措施来防止以下风险:
1)名称冲突,另一个公司的共享程序集执行的类型与自己的共享程序集中的类型同名。因为客户端代码理论上可以同时访问这些程序集,所以这是一个很严重的问题。
2)程序集被同一个程序集的不同版本覆盖--新版本与某些已有的客户端代码不兼容。
1.5 .NET Framework类
1.6 名称空间
如果没有显式提供名称空间,类型就添加到一个没有名称的全局名称空间中。
1.7 用C#创建.NET应用程序
1)创建ASP.NET应用程序。
2)创建Windows窗体。
3)使用MPF。
4)Windows控件。
5)Windows服务。
6)WCF。
7)Windows WF。
1.8 C#在.NET企业体系结构中的作用
2. 核心C#
2.2 变量
1)变量的初始化
C#编译器需要用某个初始值对变量进行初始化,之后才能在操作中引用该变量。大多数现代编译器把没有初始化标记为警告,单C#编译器把它当作错误来看待。
C#有两个方法可确保变量在使用前进行了初始化:
变量是类或结构中的字段,如果没有显式初始化,创建这些变量时,其默认值就是0。
方法的局部变量必须在代码中显示初始化,之后才能在语句中使用它们的值。
2)类型推断
类型推断使用var关键字。声明变量的语法有些变化。编译器可以根据变量的初始化值”推断“变量的类型。
需要遵循一些规则:
变量必须初始化。否则,编译器就没有推断变量类型的依据。
初始化不能为空。
初始化器必须放在表达式中。
不能把初始化设置为一个对象,除非在初始化器中创建了一个新对象。
注:声明了变量,推断出了类型后,就不能改变变量类型了。变量的类型确定后,就遵循其它变量类型遵循的强化类型化规则。
3)常量
常量总是静态的。但注意,不必(实际上是不允许)在常量声明中包含修饰符static。
2.3 预定义数据类型
C#把数据类型分为两种:值类型和引用类型。值类型存储在堆栈中,而引用类型存储在托管堆上。特殊的类型是,结构体是值类型,字符串是引用类型。
如果对一个整数是int、uint、long或ulong没有任何显式的生命,则该变量默认为int类型。为了把输出的值指定为其它整数类型,可以在数字后面加上后缀。
如果在代码中没有对某个非整数值硬编码,则编译器一般假定改变量是double。如果想指定该值为float,可以在其后加上字符F(或f)。
decimal类型标识精度更高的浮点数,但应注意,decimal类型不是基本类型,所以在计算时使用该类型会有性能损失。后缀是M(或m)。
bool值和整数值不能相互隐式转换。只能使用值true和false。如果试图使用0表示false,非0值表示true,就会出错。
char为了保存单个字符的值。
object类型就是最终的父类型,所有内置类型和用户定义的类型都从它派生而来。这样,object类型就可以用于两个目的:
可以使用object引用绑定任何子类型的对象。
object类型执行许多一般用途的基本方法,包括Equals、GetHashCode、GetType和ToString。
string是一个引用类型,当把一个字符变量赋予另一个字符串时,会得到对内存中同一个字符串的两个引用,但是字符串是不可改变的。修改其中一个字符串,就会创建一个全新的string对象,而另一个字符串不发生任何变化。
可以再字符串变量的前面加上字符@,在这个字符后的所有字符都看作是其原来的含义--它们不会解释为转义字符。
2.4 流控制
C#中的switch语句与C++中的switch语句的另一个不同之处:在C#中,可以把字符串用作测试的变量。
foreach循环不能改变集合中各项的值,如果需要迭代集合中的各项,并改变它们的值,就应使用for循环。
2.5 枚举
枚举是用户定义的整数类型。枚举的真正强大之处是它们在后台会实例化为派生于基类System.Enum的结构。这表示可以对它们调用方法,执行有用的任务。
在语法上把枚举当作结构是不会造成性能损失。实际上,一旦代码编译好,枚举就成为基本类型,与int和float类似。
可以获取枚举的字符串表示,还可以从字符串中获取枚举值。
2.6 名称空间
名称空间提供了一种组织相关类和其它类型的方式。名称空间是一种逻辑组合,而不是物理组合。
名称空间可以引用其它名称空间,用using关键字;名称空间内也可以嵌套定义名称空间。
注意:不允许在另一个嵌套的名称空间中声明多部分的名称空间。
名称空间与程序集无关。同一个程序集中可以有不同的名称空间,也可以在不同的程序集中定义同一名称空间中的类型。
如果using语句引用的两个名称空间包含同名的类型,就必须使用完整的名称(或者至少较长的名称),确保编译器知道访问哪个类型。
using语句与C++中的#include语句相混淆是错误,using语句在这些文件之间并没有建立物理链接。C#也没有对应于C++头文件的部分。
using关键字的另一个用途是给类和名称空间指定别名。
2.10 注释
根据特定的注释自动创建XML格式的文档说明。这些注释都是单行注释,单都以斜杠(///)开头,而不是通常的两条斜杠。在这些注释中,可以把包含类型和类型成员的文档说明的XML标记放在代码中。
2.11 C#预处理指令
C#并没有像C++那样独立的预处理器,所谓的预处理指令实际上是由编译器处理的。尽管如此,C#扔保留了一些预处理器指令名称,因为这些命令会让人觉得就是预处理器。
2.12 C#编程规范
变量名称定义:可以包含数字字符,但是它们必须以字母或下划线开头。并且不能把C#关键字用作标识符,如果必须使用标识符作为名称,需要在前面加@前缀。标识符也可以包含Unicode字符。
命名变量时不使用任何前缀。
3 对象和类型
3.1 类和结构
类和结构实际上都是创建对象的模板,每个对象都包含数据,并提供了处理和访问数据的方法。
结构和类的区别是它们在内存中的存储方式、访问方式、(类是存储在堆上的引用类型,而结构是存储在栈上的值类型)和他们的一些特征(如结构不支持继承 )。
较小的数据类型使用结构可提高性能。
类和结构都使用关键字new来声明实例:这个关键字创建对象并对其进行初始化。
3.2 类
类中的数据和函数成为类的成员。
3.2.1 数据成员
数据成员是包含类的数据-----字段、常量和事件的成员。数据成员可以是静态数据。类成员总是实例成员,类成员总是实例成员,除非用static进行显式声明。
字段是与类相关的变量。
常量与类的关联方式同变量与类的关联方式。使用const关键字来声明常量。
事件是类的成员、在发生某些行为(如改变类的字段或属性,或者进行了某种形式的用户交互操作)时,它可以让对象通知调用方。
3.2.2 函数成员
函数提供了操作类中数据的某些功能,包括方法、属性、构造函数和终结器、运算符以及索引器。
方法是与某个类相关的函数,与数据成员一样,函数成员默认为实例成员,使用static修饰符可以把方法定义为静态方法。
属性是可以从客户端访问的函数组,其访问方式与访问类的公共字段类似。属性的语法不同于一般函数的语法,在客户端代码中,虚拟的对象被当做实际的东西。
构造函数是实例化对象时自动调用的特殊函数。它们必须与所属的类同名,且不能有返回类型。构造函数用于初始化字段的值。
终结器类似于构造函数,但是在CLR检测到不再需要某个对象时调用它。它们的名称与类相同,但前面有一个“~”符号。不可能预测什么时候调用终结器。
运算符执行的最简单的操作就是加法和减法。在两个整数相加时,严格地说,就是对整数使用“+”运算符。
索引器允许对象以数组或集合的方式进行索引。
变量通过引用传递给方法时,被调用的方法得到的就是这个变量,所以在方法内部对变量进行的任何改变在方法退出后仍旧有效。
变量通过值传送给方法时,被调用的方法得到的是变量的一个相同的副本,在方法退出后,对变量进行的修改会丢失。
对于复杂的数据类型,按引用传递的效率更高,因为在按值传递时,必须复制大量的数据。
在C#中,除非特别说明,所有的参数都通过值来传递。
注意字符串的行为方式有所不同,因为字符串是不可变的(如果改变字符串的值,就会创建一个全新的字符串),所以字符串无法采用一般引用类型的行为方式。
在方法调用中,对字符串所做的任何改变都不会影响原始字符串。
通过值传送变量是默认的,也可以使用ref迫使值参数通过引用传送给方法。
C#要求对传递给方法的参数进行初始化,无论是按值传递,还是按引用传递,任何变量都必须初始化。
在方法的输入参数前面加上out前缀时,传递给该方法的变量可以不初始化。
参数一般需要按定义的顺序传送给方法,命名参数允许按任意顺序传递。
在属性定义中,get访问器不带任何参数,且必须返回属性声明的类型。也不应为set访问器指定任何显式参数,但是编译器假定它带一个参数,其类型也与属性相同,并表示为value。属性的访问可以添加修饰符,如果属性的set和get访问器中没有任何逻辑,就可以使用自动实现的属性。这种属性会自动实现后备成员变量。自动实现的属性必须带有两个访问器。
3.4 结构
结构是值类型,不是引用类型。它们存储在栈中或存储为内联(如果它们是存储在堆中的另一个对象的一部分),其生存期的限制与简单的数据类型一样。
结构不支持继承。
对于结构构造函数的工作方式有一些区别,尤其是编译器总是提供一个无参数的默认构造函数,它是不允许替换的。
使用结构,可以指定字段如何在内存中的布局。
虽然结构是值类型,但在语法上常常可以把他们当作类来处理。