C#与NET的关系.docx
- 文档编号:14984060
- 上传时间:2023-06-29
- 格式:DOCX
- 页数:31
- 大小:48.66KB
C#与NET的关系.docx
《C#与NET的关系.docx》由会员分享,可在线阅读,更多相关《C#与NET的关系.docx(31页珍藏版)》请在冰点文库上搜索。
C#与NET的关系
目录
第1章.NET体系结构2
1.1 C#与.NET的关系2
1.2 公共语言运行库2
1.2.1 平台无关性2
1.2.2 提高性能3
1.2.3 语言的互操作性3
1.3 中间语言4
1.3.1 面向对象和接口的支持4
1.3.2 相异值类型和引用类型5
1.3.3 强数据类型5
1.3.4 通过异常处理错误10
1.3.5 特性的使用10
1.4 程序集10
1.4.1 私有程序集11
1.4.2 共享程序集11
1.4.3 反射11
1.5 .NETFramework类12
1.6 命名空间12
1.7 用C#创建.NET应用程序13
1.7.1 创建ASP.NET应用程序13
1.7.2 创建Windows窗体14
1.7.3 使用WindowsPresentationFoundation(WPF)14
1.7.4 Windows控件15
1.7.5 Windows服务15
1.7.6 WindowsCommunicationFoundation(WCF)15
1.8 C#在.NET企业体系结构中的作用15
1.9 小结16
第1章.NET体系结构
我们不能孤立地使用C#语言,而必须和.NETFramework一起考虑。
C#编译器专门用于.NET,这表示用C#编写的所有代码总是在.NETFramework中运行。
对于C#语言来说,可以得出两个重要的结论:
(1)C#的结构和方法论反映了.NET基础方法论。
(2)在许多情况下,C#的特定语言功能取决于.NET的功能,或依赖于.NET基类。
由于这种依赖性,在开始使用C#编程前,了解.NET的结构和方法论就非常重要了,这就是本章的目的。
下面是本章的内容:
● 本章首先了解在.NET编译和运行所有的代码(包括C#)时通常会出现什么情况。
● 对这些内容进行概述之后,就要详细阐述Microsoft中间语言(Microsoft
IntermediateLanguage,MSIL或简称为IL),.NET上所有编译好的代码都要使用这种语言。
本章特别要介绍IL、通用类型系统(CommonTypeSystem,CTS)及公共语言规范(CommonLanguageSpecification,CLS)如何提供.NET语言之间的互操作性。
最后解释各种语言如何使用.NET,包括VisualBasic和C++。
● 之后,我们将介绍.NET的其他特性,包括程序集、命名空间和.NET基类。
● 最后本章简要探讨一下C#开发人员可以创建的应用程序类型。
1.1 C#与.NET的关系
C#是一种相当新的编程语言,C#的重要性体现在以下两个方面:
● 它是专门为与Microsoft的.NETFramework一起使用而设计的。
(.NETFramework是一个功能非常丰富的平台,可开发、部署和执行分布式应用程序)。
● 它是一种基于现代面向对象设计方法的语言,在设计它时,Microsoft还吸取了其他类似语言的经验,这些语言是近20年来面向对象规则得到广泛应用后才开发出来的。
有一个很重要的问题要弄明白:
C#就其本身而言只是一种语言,尽管它是用于生成面向.NET环境的代码,但它本身不是.NET的一部分。
.NET支持的一些特性,C#并不支持。
而C#语言支持的另一些特性,.NET却不支持(例如运算符重载)!
但是,因为C#语言是和.NET一起使用的,所以如果要使用C#高效地开发应用程序,理解Framework就非常重要,所以本章将介绍.NET的内涵。
1.2 公共语言运行库
.NETFramework的核心是其运行库的执行环境,称为公共语言运行库(CLR)或.NET运行库。
通常将在CLR的控制下运行的代码称为托管代码(managedcode)。
但是,在CLR执行编写好的源代码之前,需要编译它们(在C#中或其他语言中)。
在.NET中,编译分为两个阶段:
(1)把源代码编译为Microsoft中间语言(IL)。
(2)CLR把IL编译为平台专用的代码。
这个两阶段的编译过程非常重要,因为Microsoft中间语言(托管代码)是提供.NET的许多优点的关键。
Microsoft中间语言与Java字节代码共享一种理念:
它们都是低级语言,语法很简单(使用数字代码,而不是文本代码),可以非常快速地转换为内部机器码。
对于代码来说,这种精心设计的通用语法有很重要的优点:
平台无关性、提高性能和语言的互操作性。
1.2.1 平台无关性
首先,这意味着包含字节代码指令的同一文件可以放在任一平台中,运行时编译过程的最后阶段可以很容易完成,这样代码就可以运行在特定的平台上。
换言之,编译为中间语言就可以获得.NET平台无关性,这与编译为Java字节代码就会得到Java平台无关性是一样的。
注意.NET的平台无关性目前只是一种可能,因为在编写本书时,.NET只能用于Windows平台,但人们正在积极准备,使它可以用于其他平台(参见Mono项目,它用于实现.NET的开放源代码,参见http:
//www.go-
1.2.2 提高性能
前面把IL和Java做了比较,实际上,IL比Java字节代码的作用还要大。
IL总是即时编译的(称为JIT编译),而Java字节代码常常是解释性的,Java的一个缺点是,在运行应用程序时,把Java字节代码转换为内部可执行代码的过程会导致性能的损失(但在最近,Java在某些平台上能进行JIT编译)。
JIT编译器并不是把整个应用程序一次编译完(这样会有很长的启动时间),而是只编译它调用的那部分代码(这是其名称由来)。
代码编译过一次后,得到的内部可执行代码就存储起来,直到退出该应用程序为止,这样在下次运行这部分代码时,就不需要重新编译了。
Microsoft认为这个过程要比一开始就编译整个应用程序代码的效率高得多,因为任何应用程序的大部分代码实际上并不是在每次运行过程中都执行。
使用JIT编译器,从来都不会编译这种代码。
这解释了为什么托管IL代码的执行几乎和内部机器代码的执行速度一样快,但是并没有说明为什么Microsoft认为这会提高性能。
其原因是编译过程的最后一部分是在运行时进行的,JIT编译器确切地知道程序运行在什么类型的处理器上,可以利用该处理器提供的任何特性或特定的机器代码指令来优化最后的可执行代码。
传统的编译器会优化代码,但它们的优化过程是独立于代码所运行的特定处理器的。
这是因为传统的编译器是在发布软件之前编译为内部机器可执行的代码。
即编译器不知道代码所运行的处理器类型,例如该处理器是x86兼容处理器还是Alpha处理器,这超出了基本操作的范围。
例如VisualStudio6为一般的奔腾机器进行了优化,所以它生成的代码就不能利用奔腾III处理器的硬件特性。
相反,JIT编译器不仅可以进行VisualStudio6所能完成的优化工作,还可以优化代码所运行的特定处理器。
1.2.3 语言的互操作性
使用IL不仅支持平台无关性,还支持语言的互操作性。
简而言之,就是能将任何一种语言编译为中间代码,编译好的代码可以与从其他语言编译过来的代码进行交互操作。
那么除了C#之外,还有什么语言可以通过.NET进行交互操作呢?
下面就简要讨论其他常见语言如何与.NET交互操作。
1.VisualBasic2008
VisualBasic6在升级到VisualBasic.NET2002时,经历了一番脱胎换骨的变化,才集成到.NETFramework的第一版中。
VisualBasic语言对VisualBasic6进行了很大的演化,也就是说,VisualBasic6并不适合运行.NET程序。
例如,它与COM的高度集成,且只把事件处理程序作为源代码显示给开发人员,大多数后台代码不能用作源代码。
另外,它不支持继承,VisualBasic使用的标准数据类型也与.NET不兼容。
VisualBasic6在2002年升级为VisualBasic.NET,对VisualBasic进行的改变非常大,完全可以把VisualBasic当作是一种新语言。
现有的VisualBasic6代码不能编译为VisualBasic2008代码(或VisualBasic.NET2002、2003和2005代码),把VisualBasic6程序转换为VisualBasic2008时,需要对代码进行大量的改动,但大多数修改工作都可以由VisualStudio2008(VisualStudio的升级版本,用于与.NET一起使用)自动完成。
如果把VisualBasic6项目读到VisualStudio2008中,VisualStudio2008就会升级该项目,也就是说把VisualBasic6源代码重写为VisualBasic2008源代码。
虽然这意味着其中的工作已大大减轻,但用户仍需要检查新的VisualBasic2008代码,以确保项目仍可正确工作,因为这种转换并不十分完美。
这种语言升级的一个副作用是不能再把VisualBasic2008编译为内部可执行代码了。
VisualBasic2008只编译为中间语言,就像C#一样。
如果需要继续使用VisualBasic6编写程序,就可以这么做,但生成的可执行代码会完全忽略.NETFramework,如果继续把VisualStudio作为开发环境,就需要安装VisualStudio6。
2.VisualC++2008
VisualC++6有许多Microsoft对Windows的特定扩展。
通过VisualC++.NET,又加入了更多的扩展内容,来支持.NETFramework。
现有的C++源代码会继续编译为内部可执行代码,不会有修改,但它会独立于.NET运行库运行。
如果让C++代码在.NETFramework中运行,就可以在代码的开头添加下述命令:
#using
还可以把标记/clr传递给编译器,这样编译器假定要编译托管代码,因此会生成中间语言,而不是内部机器码。
C++的一个有趣的问题是在编译托管代码时,编译器可以生成包含内嵌本机可执行代码的IL。
这表示在C++代码中可以把托管类型和非托管类型合并起来,因此托管C++代码:
classMyClass
{
定义了一个普通的C++类,而代码:
_refclassMyClass
{
生成了一个托管类,就好像使用C#或VisualBasic2008编写类一样。
实际上,托管C++比C#更优越的一点是可以在托管C++代码中调用非托管C++类,而不必采用COM交互功能。
如果在托管类型上试图使用.NET不支持的特性(例如,模板或类的多继承),编译器就会出现一个错误。
另外,在使用托管类时,还需要使用非标准的C++特性。
因为C++允许低级指针操作,C++编译器不能生成可以通过CLR内存类型安全测试的代码。
如果CLR把代码标识为内存类型安全是非常重要的,就需要用其他一些语言编写源代码,例如C#或VisualBasic2008。
3.COM和COM+
从技术上讲,COM和COM+并不是面向.NET的技术,因为基于它们的组件不能编译为IL(但如果原来的COM组件是用C++编写的,使用托管C++,在某种程度上可以这么做)。
但是,COM+仍然是一个重要的工具,因为其特性没有在.NET中完全实现。
另外,COM组件仍可以使用--.NET集成了COM的互操作性,从而使托管代码可以调用COM组件,COM组件也可以调用托管代码(见第24章)。
在一般情况下,把新组件编写为.NET组件,大多是为了方便,因为这样可以利用.NET基类和托管代码的其他优点。
1.3 中间语言
如前所述,Microsoft中间语言显然在.NETFramework中有非常重要的作用。
C#开发人员应明白,C#代码在执行前要编译为中间语言(实际上,C#编译器仅编译为托管代码),这是有意义的,现在应详细讨论一下IL的主要特征,因为面向.NET的所有语言在逻辑上都需要支持IL的主要特征。
下面就是中间语言的主要特征:
● 面向对象和使用接口
● 值类型和引用类型之间的巨大差别
● 强数据类型
● 使用异常来处理错误
● 使用特性(attribute)
下面详细讨论这些特征。
1.3.1 面向对象和接口的支持
.NET的语言无关性还有一些实际的限制。
中间语言在设计时就打算实现某些特殊的编程方法,这表示面向它的语言必须与编程方法兼容,Microsoft为IL选择的特定道路是传统的面向对象的编程,带有类的单一继承性。
注意:
不熟悉面向对象概念的读者应参考附录B,获得更多的信息。
除了传统的面向对象编程外,中间语言还引入了接口的概念,它们显示了在带有COM的Windows下的第一个实现方式。
.NET接口与COM接口不同,它们不需要支持任何COM基础结构,例如,它们不是派生自IUnknown,也没有对应的GUID。
但它们与COM接口共享下述理念:
提供一个契约,实现给定接口的类必须提供该接口指定的方法和属性的实现方式。
前面介绍了使用.NET意味着要编译为中间语言,即需要使用传统的面向对象的方法来编程。
但这并不能提供语言的互操作性。
毕竟,C++和Java都使用相同的面向对象的范型,但它们仍不是可交互操作的语言。
下面需要详细探讨一下语言互操作性的概念。
首先,需要确定一下语言互操作性的含义。
毕竟,COM允许以不同语言编写的组件一起工作,即可以调用彼此的方法。
这就足够了吗?
COM是一个二进制标准,允许组件实例化其他组件,调用它们的方法或属性,而无须考虑编写相关组件的语言。
但为了实现这个功能,每个对象都必须通过COM运行库来实例化,通过接口来访问。
根据相关组件的线程模型,不同线程上内存空间和运行组件之间要编组数据,这还可能造成很大的性能损失。
在极端情况下,组件保存为可执行文件,而不是DLL文件,还必须创建单独的进程来运行它们。
重要的是组件要能与其他组件通信,但仅通过COM运行库进行通信。
无论COM是用于允许使用不同语言的组件直接彼此通信,或者创建彼此的实例,系统都把COM作为中间件来处理。
不仅如此,COM结构还不允许利用继承实现,即它丧失了面向对象编程的许多优势。
一个相关的问题是,在调试时,仍必须单独调试用不同语言编写的组件。
这样就不可能在调试器上调试不同语言的代码了。
语言互操作性的真正含义是用一种语言编写的类应能直接与用另一种语言编写的类通信。
特别是:
● 用一种语言编写的类应能继承用另一种语言编写的类。
● 一个类应能包含另一个类的实例,而不管它们是使用什么语言编写的。
● 一个对象应能直接调用用其他语言编写的另一个对象的方法。
● 对象(或对象的引用)应能在方法之间传递。
● 在不同的语言之间调用方法时,应能在调试器中调试这些方法调用,即调试不同语言编写的源代码。
这是一个雄心勃勃的目标,但令人惊讶的是,.NET和中间语言已经实现了这个目标。
在调试器上调试方法时,VisualStudioIDE提供了这样的工具(不是CLR提供的)。
1.3.2 相异值类型和引用类型
与其他编程语言一样,中间语言提供了许多预定义的基本数据类型。
它的一个特性是值类型和引用类型有明显的区别。
对于值类型,变量直接保存其数据,而对于引用类型,变量仅保存地址,对应的数据可以在该地址中找到。
在C++中,引用类型类似于通过指针来访问变量,而在VisualBasic中,与引用类型最相似的是对象,VisualBasic6总是通过引用来访问对象。
中间语言也有数据存储的规范:
引用类型的实例总是存储在一个名为"托管堆"的内存区域中,值类型一般存储在堆栈中(但如果值类型在引用类型中声明为字段,它们就内联存储在堆中)。
第2章"C#基础"讨论堆栈和堆,及其工作原理。
1.3.3 强数据类型
中间语言的一个重要方面是它基于强数据类型。
所有的变量都清晰地标记为属于某个特定数据类型(在中间语言中没有VisualBasic和脚本语言中的Variant数据类型)。
特别是中间语言一般不允许对模糊的数据类型执行任何操作。
例如,VisualBasic6开发人员习惯于传递变量,而无需考虑它们的类型,因为VisualBasic6会自动进行所需的类型转换。
C++开发人员习惯于在不同类型之间转换指针类型。
执行这类操作将大大提高性能,但破坏了类型的安全性。
因此,这类操作只能在某些编译为托管代码的语言中的特殊情况下进行。
确实,指针(相对于引用)只能在标记了的C#代码块中使用,但在VisualBasic中不能使用(但一般在托管C++中允许使用)。
在代码中使用指针会立即导致CLR提供的内存类型安全性检查失败。
注意,一些与.NET兼容的语言,例如VisualBasic2008,在类型化方面的要求仍比较松,但这是可以的,因为编译器在后台确保在生成的IL上强制类型安全。
尽管强迫实现类型的安全性最初会降低性能,但在许多情况下,我们从.NET提供的、依赖于类型安全的服务中获得的好处更多。
这些服务包括:
● 语言的互操作性
● 垃圾收集
● 安全性
● 应用程序域
下面讨论强数据类型化对这些.NET特性非常重要的原因。
1.语言互操作性中强数据类型的重要性
如果类派生自其他类,或包含其他类的实例,它就需要知道其他类使用的所有数据类型,这就是强数据类型非常重要的原因。
实际上,过去没有任何系统指定这些信息,从而成为语言继承和交互操作的真正障碍。
这类信息不只是在一个标准的可执行文件或DLL中出现。
假定将VisualBasic2008类中的一个方法定义为返回一个Integer--VisualBasic2008可以使用的标准数据类型之一。
但C#没有该名称的数据类型。
显然,我们只能从该类中派生,再使用这个方法,如果编译器知道如何把VisualBasic2008的Integer类型映射为C#定义的某种已知类型,就可以在C#代码中使用返回的类型。
这个问题在.NET中是如何解决的?
(1)通用类型系统(CTS)
这个数据类型问题在.NET中使用通用类型系统(CTS)得到了解决。
CTS定义了可以在中间语言中使用的预定义数据类型,所有面向.NETFramework的语言都可以生成最终基于这些类型的编译代码。
例如,VisualBasic2008的Integer实际上是一个32位有符号的整数,它实际映射为中间语言类型Int32。
因此在中间语言代码中就指定这种数据类型。
C#编译器可以使用这种类型,所以就不会有问题了。
在源代码中,C#用关键字int来表示Int32,所以编译器就认为VisualBasic2008方法返回一个int类型的值。
通用类型系统不仅指定了基本数据类型,还定义了一个内容丰富的类型层次结构,其中包含设计合理的位置,在这些位置上,代码允许定义它自己的类型。
通用类型系统的层次结构反映了中间语言的单一继承的面向对象方法,如图1-1所示。
图 1-1
这个树形结构中的类型说明如表1-1所示。
表 1-1
类型
含义
Type
代表任何类型的基类
ValueType
代表任何值类型的基类
ReferenceTypes
通过引用来访问,且存储在堆中的任何数据类型
Built-inValueTypes
包含大多数标准基本类型,可以表示数字、Boolean值或字符
Enumerations
枚举值的集合
User-definedValueTypes
在源代码中定义,且保存为值类型的数据类型。
在C#中,它表示结构
InterfaceTypes
接口
PointerTypes
指针
Self-describingTypes
为垃圾回收器提供信息的数据类型(参见下一节)
Arrays
包含对象数组的类型
ClassTypes
可自我描述的类型,但不是数组
Delegates
用于把引用包含在方法中的类型
User-definedReferenceTypes
在源代码中定义,且保存为引用类型的数据类型。
在C#中,它表示类
BoxedValueTypes
值类型,临时打包放在一个引用中,以便于存储在堆中
这里没有列出内置的所有值类型,因为第3章将详细介绍它们。
在C#中,编译器识别的每个预定义类型都映射为一个IL内置类型。
这与VisualBasic2008是一样的。
(2)公共语言规范(CLS)
公共语言规范(CommonLanguageSpecification,CLS)和通用类型系统一起确保语言的互操作性。
CLS是一个最低标准集,所有面向.NET的编译器都必须支持它。
因为IL是一种内涵非常丰富的语言,大多数编译器的编写人员有可能把给定编译器的功能限制为只支持IL和CLS提供的一部分特性。
只要编译器支持已在CLS中定义的内容,这就是很不错的。
提示:
编写非CLS兼容代码是完全可以接受的,只是在编写了这种代码后,就不能保证编译好的IL代码完全支持语言的互操作性。
下面的一个例子是有关区分大小写字母的。
IL是区分大小写的语言。
使用这些语言的开发人员常常利用区分大小写所提供的灵活性来选择变量名。
但VisualBasic2008是不区分大小写的语言。
CLS就要指定CLS兼容代码不使用任何只根据大小写来区分的名称。
因此,VisualBasic2008代码可以与CLS兼容代码一起使用。
这个例子说明了CLS的两种工作方式。
首先是各个编译器的功能不必强大到支持.NET的所有功能,这将鼓励人们为其他面向.NET的编程语言开发编译器。
第二,它提供如下保证:
如果限制类只能使用CLS兼容的特性,就要保证用其他兼容语言编写的代码可以使用这个类。
这种方法的优点是使用CLS兼容特性的限制只适用于公共和受保护的类成员和公共类。
在类的私有实现方式中,可以编写非CLS代码,因为其他程序集(托管代码的单元,参见本章后面的内容)中的代码不能访问这部分代码。
这里不深入讨论CLS规范。
在一般情况下,CLS对C#代码的影响不会太大,因为C#中的非CLS兼容特性非常少。
2.垃圾收集
垃圾收集器用来在.NET中进行内存管理,特别是它可以恢复正在运行中的应用程序需要的内存。
到目前为止,Windows平台已经使用了两种技术来释放进程向系统动态请求的内存:
● 完全以手工方式使应用程序代码完成这些工作。
● 让对象维护引用计数。
让应用程序代码负责释放内存是低级高性能的语言使用的技术,例如C++。
这种技术很有效,且可以让资源在不需要时就释放(一般情况下),但其最大的缺点是频繁出现错误。
请求内存的代码还必须显式通知系统它什么时候不再需要该内存。
但这是很容易被遗漏的,从而导致内存泄漏。
尽管现代的开发环境提供了帮助检测内存泄漏的工具,但它们很难跟踪错误,因为直到内存已大量泄漏从而使Windows拒绝为进程提供资源时,它们才会发挥作用。
到那个时候,由于对内存的需求很大,会使整个计算机变得相当慢。
维护引用计数是COM对象采用的一种技术,其方法是每个COM组件都保留一个计数,记录客户机目前对它的引用数。
当这个计数下降到0时,组件就会删除自己,并释放相应的内存和资源。
它带来的问题是仍需要客户机通知组件它们已经完成了内存的使用。
只要有一个客户机没有这么做,对象就仍驻留在内存中。
在某些方面,这
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C# NET 关系