UML软件工程组织

.NET 简介、Hello World 和 .NET Runtime 一瞥

 

作者:Dr. GUI  文章来源:microsoft

 

  什么是 .NET
  Dr. GUI 对 .NET 框架的回顾
  全新的开始而不用从头再来:.NET Runtime
  现在学习 .NET 的原因
  运行 Beta 1 所需的条件
  编写 .NET 程序
  Hello World.NET
  实践与探讨

Dr. GUI 回来写教程了!

 如果您是 Dr. GUI 的 COM 和 ALT 系列栏目的忠实读者,那么在过去的几个月中或许会想念博士的联机专栏文章。(对了,COM 和 ALT 系列的提前结束要归咎于 Dr. GUI 的经理给博士安排了一项关于电子商务的工作。)

但是至少 Dr. GUI 还继续在 MSDN 新闻(英文)中主持“Ask Dr. GUI”栏目。现在,Dr. GUI 又有了新任务:帮助大家学习 .NET 框架。

那为什么是 Dr. GUI.NET 呢?

 “Ask Dr. GUI”的热心读者都知道 Dr. GUI 特别热衷于 Microsoft .NET。现在我们的博士也搞不清楚 .NET 到底包含什么不包含什么。(如果您想弄清楚这一点,请浏览 .NET Web 的正式站点 http://www.microsoft.com/net/。)这种命名的混乱让人回想起 ActiveX? 时代。只要有促销活动,就会有命名的混乱。Dr. GUI 赞成“ActiveX”中的“X”不发声的主张,只可惜这主张已是昨日黄花。

不管我们的博士是否清楚什么属于 .NET 而什么不属于,以及在未来几年中它还会发生多大的变化,他确实理解对于开发人员至关重要的关键部分:.NET 框架。倒不仅仅是因为他“获取”了这个部分,而是他为之兴奋不已:它将使开发工作更有趣味也更有成效。而且,.NET 框架在未来的几年中将不断获得改进,它不会消亡的。

.NET 框架的不幸之处就是迄今为止它尚未上市。现在仅仅能够得到技术和 Visual Studio.NET 的第一个 Beta 版,您可以在 .NET 的大本营:MSDN 的 .NET 开发人员中心的网站 http://msdn.microsoft.com/net/ 上获得它。您可以只要 .NET 框架SDK 或者连同整个 Visual Studio.NET。Visual Studio.NET 包括了 .NET 框架SDK。这一点我们以后再聊。

它是真的吗?

 您或许会想起 Dr. GUI 有一个原则,就是不为那些不真实的东西写文章。他很少冒险违反这个原则。(我们的博士曾经为 SQL Server? 2000 和 Windows? 2000 而恼火,他不得不在它们上市之前就写了大量文章。事实证明博士基本上是正确的,大多人只有到能看到实际的产品时才能理解产品。直到现在,他还在为被称为“Dr. Hormel”而伤心。)

那为什么 Dr. GUI 现在就要为 .NET 写文章呢?

原因非常简单:.NET 还未上市,不过它很是真实。您可以成为圈子里第一个下载 .NET 框架(或得到 CD)的人,然后自己先试用一下。您不必把博士的话当真。如果您不是要编写明天就得交付的 .NET 应用程序,也不太介意风险的话,您可以从现在着手,开始用它来进行 2001 年晚些时候才需交付的新项目。这样,您就可以先利用预发布版开始学习 .NET,在它上市之前就做好准备,领先您的同行一大步。

什么是 .NET?

 现在要指出什么是 .NET 而什么不是还很困难。但是,如果您理解是哪些关键因素推动着 .NET,您也就更容易了解 .NET。

世界的变化

 想想我们这个行业里出现的一些趋势。很多人正在抛弃没有联网的单台 PC 机,开始使用通过 Internet 同时连接到多个应用程序的 PC 机以及各种名目繁多的设备。连接到 Internet 的方式多种多样,有线的、无线的,基带的、宽带的。他们开始期望无论身处何地,无论使用何种设备,都能够以适当的形式获得他们的重要信息。并且,既然能够通过 Internet 获得信息,他们就希望能把从多个来源获得的数据合成为有意义的信息。最后,他们期望能够从容地使用软件而不必忙于安装和升级,他们还期望这一切远远比过去的 PC 机可靠。

那还只是在客户端机器上发生的变化。Internet 已经使基于服务器的应用程序得到空前广泛的应用。但是服务器之间的通讯,尤其是在不同平台上运行的服务器之间的通讯特别困难。例如,处理不同的浏览器和浏览器的不同版本时是多么麻烦,更甭提各种不同的无线设备。并且因为大多数真实的 Internet 应用程序都是以数据库为中心的,方便地访问多种的数据源的能力比以往任何时候都更加重要。

.NET 如何帮助您适应变化

 .NET 框架可以使您更轻松地实现这类功能。它包括用于设备的 .NET Compact Framework,这使得在各种设备上而不仅仅是在 PC 上利用 .NET Runtime 成为可能。从最底层开始,.NET 的设计就是以适应 Internet 的需要为目标,融合了各种 Internet 标准,包括对 Web 服务的支持,使程序可以方便地从多个站点上获取数据;广泛运用 XML 语言,使得不同计算机系统之间的数据互换变得简单易行。.NET 还使您能够更容易地访问数据,更容易地编写出能在众多不同浏览器和设备上良好运行的 Web 应用程序,而不用再为每种设备或浏览器分别编写程序。最后,.NET Runtime 支持简化和可靠的安装和升级,使用更简便,可靠性更好。它和 .NET Runtime 的安全性及版本管理功能相结合,也提高了可靠性,并使软件可以作为一种服务而不是产品来出售。

使用 Web 服务将数据从多个数据源集成为可以使用的信息

 另一个重要的趋势是应用程序为达到某种目的而从多个数据源集成信息。典型的示例就是计划一个商务旅行:您需要机票、租用汽车和酒店。您想去商店购物,并希望所需的每一种商品的价格都符合您的标准,所以需要从三组不同公司中获得数据;您的旅行还需要调整,以便按时到达,且到达时有汽车、有酒店房间(当然要有以太网),并且能及时回家。Web 服务使用 SOAP 和标准的 XML 架构帮助应用程序以业界标准的方式在 Internet 上通讯,所以应用程序可以使用并共享另一台计算机上的信息,而不管另一台计算机所使用的平台。.NET 框架使编写和使用 Web 服务成为一件非常简单的事情。但是,如果您想要看用 .NET 实现复杂的真实 Web 服务示例,请打开网站 http://terraserver.microsoft.net/,上面展示了世界各地的地图和航拍照片信息(Microsoft 的 TerraService 工程提供)以及其他信息,如美国人口普查数据。

.NET 还包括 .NET Enterprise Servers 服务器软件,它集成了 .NET 的功能并使它更易于使用。您可以在网站 http://www.microsoft.com/servers/net/ 上学习更多有关 .NET Enterprise Servers 的知识。

最后还要说明重要的一点,Microsoft 要提供诸如身份验证和日历服务等功能作为 Web 服务,您可以像使用本地对象一样方便地使用它。如果能够以可负担的价格购买所需的功能模块,使用 OPWS(其他人员的 Web 服务,SEC [其他人的代码] 的变种)通常是件好事。您也许有兴趣了解第一个 Microsoft Web 服务,即 Passport 身份验证服务 (http://www.passport.com/Consumer/default.asp?PPlcid=1033),根据点击次数计算,即使是今天,该网站仍是十大 Web 站点之一。但是点击次数是由使用 Passport 来验证用户身份的 Web 站点(例如 Hotmail)在幕后生成的。

所以,.NET 框架使编写新型的应用程序变得更简单,这类应用程序容易安装,并比以前的应用程序更可靠。因为这些程序利用了 Microsoft 和其他公司/人(也许就是您)提供的 Web Services,因此非常强大。

Dr. GUI 对 .NET 框架的回顾

 Microsoft 操作系统 API 集的简要历史

 使用 .NET 框架编程不同于使用 Win32? API 编程,正如 Windows 编程与 DOS 编程大相径庭。每一个 API 都是某一个时代的产品:DOS API 是八十年代早期的产品;Windows API 是八十年代中期的产品;而 .NET API 是九十年代后期的产品。

DOS

 回溯 DOS API 的设计目的,是用于小型计算机系统上众多单一任务操作系统。尽管在微机问世的时代很流行使用 UNIX 和 VMS 操作系统,但微机没有足够的内存或速度来运行这些相对来说较复杂的操作系统。由于内存和速度的限制,诸如 DOS 之类的操作系统主要用汇编语言编写,所以它们尽可能地小而快。DOS 的 API 主要是绝对需要汇编语言(可能通过使用至少部分使用汇编语言编写的运行时库)来调用的软件中断调用(还记得 Int 21h 中断吗?)的集合,系统只提供了数量很少(低分)的服务,主要是面向磁盘和字符输入/输出模式。DOS 只支持单一任务(因此,只能执行单线程),并且 DOS 程序始终控制着计算机系统,当程序无事可做的时候,就循环检测是否有键盘输入。

Windows

 Windows API 设计于八十年代早期,那时的美国总统是里根,C 语言是绝大多数系统程序员的首选语言。(某些重要的系统都是用 Pascal 语言编写的,其性能和 C 语言相似。)Stroustrup 的 C++ 编程语言直到 1986 年才出版。那时正在风行“结构化编程”,程序员们学习如何避免使用 goto,直到九十年代,面向对象的编程才开始成为主流。

结果是,只有 C(或 Pascal)程序员才会喜欢 Win32 API。有且仅有一个名称空间,所有的数百个(后来是数千个)API 名称很长,且 API 编写者提出不同的命名惯例以将其分类。这些语言不直接支持抽象的数据类型,所以程序员又提出命名方案,例如 Microsoft 的匈牙利方案(Charles Simonyi 提出),在标识符的前面添加缩写前缀来表示抽象的变量类型(如“i”表示索引,“c”表示计数,它们都用作基础语言类型“int”)。

但是在 Windows 中最重要的差别是使用了图形用户界面,因此必须完全重新调整程序的结构,因为 Windows 支持多任务(尽管不是在 Win16 中的多线程),当程序无事可做的时候,将控制权返回给操作系统。所以,不是由程序始终保持控制,而是将程序重新调整为消息句柄的集合,每个句柄都对来自操作系统的消息(例如,单击鼠标和按键)做出回应,并尽可能快地将回应返回给操作系统,再等待下一条消息。在重新调整应用程序结构以后,Windows 的诸多特性开始发挥作用,如设备无关性和强大的标准化用户界面功能,包括窗口、对话框、控件(如按钮、列表框和编辑控件)以及菜单。

面向对象的编程

 在九十年代早期,开始流行面向对象的编程。通过面向对象的编程,可以编写在程序的对象之间具有较好定义的交互和极少量未定义的交互的较大项目。并且对象允许程序员以更有效的方式创建抽象概念,使编程较之以往更上一层。

Visual Basic? 在运行时封装了大量消息路由逻辑,程序员可以使用基于对象的方式,通过让 Visual Basic 程序员集中编写消息处理函数来进行 Windows 编程。当 C++ 成为流行语言时,诸如 MFC 的类库允许允许程序员使用应用程序框架提供的大量默认交互来将 Windows 应用程序编写为交互对象组。

在面向对象的编程思想广泛传播之前,用 Visual Basic 和 MFC 程序编写的 API 仍然是 Windows API。(公正地说,实际上 Windows 包含了面向对象的概念。)但是,Visual Basic 和 MFC 运行时库的大小证明了一个事实,那就是需要越来越多的努力去弥补面向对象编程思想和旧编程思想(单层 API 集)之间的差距。

组件
 九十年代中期开始流行一种新概念:软件可以由可互换的组件构成,一套组件可以装配出音频系统或家庭影院。这个观点是通过严格定义接口和根据明确定义的约定来实现的,您可以用相对容易的方法混合和装配软件的组件。

支持基于组件的编程思想要求对 Windows、OLE 和 COM 中的单层 API 附加新内容。某些附加内容非常复杂,因为基于 C 的 API 从来不会自动支持诸如软件对象的动态创建和对象的自动生存期管理等操作,不必提及 C 语言和 C++ 语言可用于编写单片机的应用程序,这些应用程序不是由一组组件构成的。

因此,编写组件成为非常乏味的工作,并且需要文件和转换器以弥补语言并不真正支持基于组件的编程这个缺憾。尽管 Visual Basic 和诸如 ATL 之类的库减轻了编写组件的乏味性,但许多高级的(但是有趣的)操作仍然相当困难。而且新版本组件常常由于 Windows 缺少支持灵活处理不同版本的特性而导致现有的应用程序中断。

如此多的功能,如此少的通用性

 最后,在这段时间里有各式各样的 API 被添加到 Windows 中,致使编程模型中出现巨大的矛盾,甚至原始的 Windows API 也出现了一些矛盾,看一下窗口、菜单和控件的处理与设备环境、画笔、字体和画刷的处理之间的差异就能了解。但更严重的问题是:在 Windows 中大概有六种(也许更多,Dr. GUI 还未仔细计数)处理错误的不同方式。大概有六种字符串类型。这仅仅是冰山的一角而已。

很清楚,在杂乱无章的 API 编程模型和对基于组件的程序和 Internet 编程的巨大支持的需求之间,恰恰是一种新型的、明确设计的、协调的现代 API 成熟的时机。Microsoft 推出了 .NET 框架以解决这些问题。

全新的开始而不用从头再来:.NET Runtime

 正如八十年代后期停止以旧的 DOS 方式编写程序一样(什么?放弃 TSR?),现在是时候让 Windows 程序员们考虑一下了,摆脱使用单层、过时的 Windows API,转而使用新型的、面向对象且基于组件的 API。Dr. GUI 建议您尝试一下 .NET 框架和 Runtime 以便了解现代的 Runtime 是如何工作的。

基于组件成为核心

 若要理解 .NET Runtime,您必须理解 .NET Runtime 的设计目的是直接在 Runtime 中为现代基于组件的编程提供第一手支持。换句话说,全部是围绕组件进行的。如果您理解这一点,理解为什么要将 .NET Runtime 设计成现在的样子就简单多了。

正如在 Windows 中添加了对窗口、控件、图形和菜单的直接支持,为基于消息的编程添加了底层结构,为支持设备无关性添加了抽象内容一样,.NET Runtime 直接支持组件(包括属性和事件)、对象、继承性、多态性和接口。它还提供支持以允许您使用二十多种不同编程语言中的任一种,仍然用非 .NET 代码交互,而且不管这些代码是在您本机上运行还是在不同的计算机上运行。换句话说,.NET 代表了一种全新的、现代化的起点,且不必从头再来:您可以使用多种语言,从熟悉的语言到外来的语言,并继续使用旧的组件和 DLL 而不用对其进行修改。您甚至可以象使用任何 COM 对象一样使用 .NET 组件,这样新的 .NET 代码可以和旧的 COM 应用程序一起使用。(您也许想要将这种语言的灵活性与易于协作性与市场上的其他运行时系统,尤其是那些以“J”开头的遗留代码对比)。

.NET Runtime 的其他主要功能的设计目的是为组件开发提供第一手支持。对属性和事件的直接支持使得基于组件的编程变得更简单,而不需要特殊的接口和适配设计模式。自动内存管理处理棘手的对象生存期问题。序列化支持允许以多种格式编写“冻干”组件,包括基于业界标准 XML 的 SOAP(并不只是专利二进制格式),并且以后重新组织它们。有了调用和创建业界标准 Web 服务的能力,您可以向 Internet 上任何地方、使用任何设备的任何人展示组件,也可以使用 Internet 上任何地方的任何设备上的组件。例外提供了处理错误条件的有力、协调的方式。每个模块都具有内置的完整元数据,这意味着诸如动态创建和方法调用之类的功能更容易,也更安全。(.NET 甚至允许您灵活地创建和执行代码。)您可以控制应用程序使用的组件的版本,这使应用程序更可靠。最后,该代码是与处理器无关的和易于验证的中间语言 (IL),而不是某一种特定的机器语言,这意味着组件不但可以在多种计算机上运行,而且可以确保组件不会覆盖它们不使用的内存,也不会潜在地导致系统崩溃。

.NET Runtime 包含这里提到的每一个关键特征,所以,您可以很容易地编写出有活力的面向对象的组件。

请稍等,还有很多内容!

 .NET Runtime 除了提供全部强大的组件支持功能外,还包括大量的类,其设计目的是提供解决现代编程问题的底层结构,包括与 ASP.NET 协同工作的复杂的、基于组件的 Web 服务器应用程序支持(并支持将各种各样的浏览器和移动设备作为客户),XML 支持,增强的数据库访问等等。

并且很快就要被标准化

 最后,.NET Runtime 的主要部分,即公共语言基础结构 (CLI) 随同 C# 语言和 IL 格式一起正处在由欧洲计算机制造协会(ECMA,标准化 JavaScript 和 JScript? 的组织,http://www.ecma.ch/ecma1/stand/ecma-262.htm)标准化的过程中,这将为其他公司在不同平台上开发兼容的 .NET 实现铺平道路。关于 C# 和 CLI 标准化的详细信息,请参见 http://msdn.microsoft.com/net/ecma/。

现在学习 .NET 的原因

 既然 .NET 还未正式上市,为什么要下载或定购 .NET 的 Beta 1 版,并且从现在就开始学习它?

简而言之,您已经有了很好的机会成为最先尝试 .NET 并在它正式上市前已经准备就绪的人。对 Dr. GUI 来说,这是令人振奋的:您看,博士以前总在 Windows、MFC、COM 和 ATL 的潮流后面苦苦追赶,他多么希望自己能够早一点学习这些技术。这一次,它充分利用这次机会率先窥探 .NET 的秘密。

抓住学习 .NET 的机会

 所有早期的迹象都表明 .NET 框架必将成为高效且大受欢迎的编程平台,所以它很快就会得到广泛应用。早一点学习,就会处于有利于地位。在其他人对它还不甚了解的时候,您已经能熟练和高效地运用这项新技术工作。顺便说一句,编写 .NET 程序既简单又有趣。

Dr. GUI 的指示:.NET 的练习每周只需一小时
 ...让程序更健壮

 因为 .NET 才出现不久,而且还未正式上市,Dr. GUI 建议您先走一步,就是:每周抽出一小时学习 .NET。

这些教程文章每月发表两次,其中包含的练习能帮助您真正地了解您所阅读的内容。所以,假设您花一小时来阅读这些文章(越往后阅读文章所花的时间会越少),一、两小时来做练习,那么您付出相对较小的努力就能学到一些有用的新技术——平均每周一小时。

每周花费一小时学习,您得到的是关于 .NET 框架的牢固知识。您将对它的工作方式了如指掌,并且在 .NET 平台上编程得心应手。

和一个(两个,或更多)朋友一起做

 一个有经验的伙伴能够帮助您处理许多问题,和您的朋友或同事一道学习 GUI.NET 将有助于您更好地完成学习,乐趣也更多。和一群人一起学就更好了。可以成立一个讨论小组,或者只是在完成每套练习时(或有问题而难以继续时)互传电子邮件。

我们将讨论的内容

 由于 .NET 框架非常巨大,您可能会感到疑惑,我们将讨论些什么,什么时候讨论?

我们将从最基本的内容开始这个专栏。您已经阅读过关于 .NET 框架要解决的问题。下一步,我们将讨论 .NET Runtime 的各个方面,包括必要的“Hello World”程序,还将简单介绍元数据和中间语言 (IL)——.NET 的机器语言。

下一次,我们将开始讨论 .NET 框架的基础,包括所有类的父类,System.Object。我们还要讨论垃圾回收和一些关于接口的内容。

在此之后,将用几篇文档讨论如何使用 .NET 框架和 Runtime 的基础:数据类型、例外、文件和网络 I/O、序列化、线程和计时器以及调试。

然后,我们将了解 Windows 程序员熟悉和喜爱的图形用户界面类。

.NET 框架中有许多用于访问数据库、XML 和 Web 应用程序的重要特征,但是博士目前计划让别人讨论这些问题。

每个专栏都有一个示例程序、示例的解释以及您可以尝试完成的练习,所有示例都用 C# 语言编写,集中说明较大的概念,以便您能够更好地理解 .NET 深处的设计技巧。

使用 C# 语言的原因

 既然 .NET 支持大约二十种语言,为什么 Dr. GUI 所有关于 .NET 的专栏都使用称之为 C# 的新语言来编写?原因非常简单:正如 C 语言是用来编写 Windows 程序的,C# 语言是用来编写 .NET 程序的。.NET 框架中的大多数内容都是使用 C# 语言编写的,但是您可以在 .NET 中使用多种语言编程,用 C# 语言是为了在 .NET 中使用和支持基于组件的编程而特别设计的。同时,C# 语言还是一种非常好的编程语言。它易于使用,但是又没有那些名称以“J”开头的语言所具有的问题。例如,您能够以 C# 语言来编写 swap 函数(在名称以“J”开头的语言中则不能这样做,这是令 Dr. GUI 对那种语言产生反感原因之一)。

另外,通过使用 C#,您在学习 .NET 的同时也学习了 C# 语言。如果您选择使用另一种语言,看以 C# 语言编写的示例不会带来负面影响:.NET 框架和 Runtime 您使用的任何语言都适用。

如果您更愿意使用其他某种语言,将代码转换为您所使用的语言相对来说比较简单。但是 Dr. GUI 这个家伙却没有时间完成这项工作。如果有人想要进行这项工作,Dr. GUI 也许能够提供到您的转换后的代码的链接——但这并非承诺。

运行 Beta 1 版所需的条件

 运行 Visual Studio.NET 的 Beta 1 版所需的条件相当简单:计算机的操作系统为 Windows 2000、Windows NT? 4.0、Windows 98 或 Windows Me。(可能会在以后的 Beta 版本中支持其他操作系统,例如 Whistler Beta 版和 Windows 95。)计算机还必须具备相当快的运算速度(要求 PII/450,建议使用 PIII/733)、1 GB 或 2 GB 硬盘空间、128 MB 内存(建议使用 256 MB 内存),至少有 Super VGA (SVGA) 视频和 CD-ROM 或 DVD-ROM 驱动器。如果您只想安装 .NET 框架SDK,CPU 和内存的要求可以稍微低一些;硬盘空间的需求可显著降低(只需 100-200 MB 就足够了)。

您还需要安装 Internet Explorer 5.5 (http://www.microsoft.com/windows/ie/)、MDAC (ADO) 2.6 (http://www.microsoft.com/data/download.htm) 和 Microsoft Web 服务器。(在控制面板中,单击“添加/删除程序”,然后单击“添加/删除 Windows 组件”。)如果您有 Visual Studio.NET 的 CD,安装程序将自动更新 Internet Explorer 和 MDAC。

安装说明建议您在非生产用途的计算机上安装。Dr. GUI 没有发现 Visual Studio.NET 和/或 .NET 框架与其他软件之间的不良影响,但是他赞成这个建议:不要在您赖以工作的计算机上冒险使用 Beta 版的软件。事实上,Dr. GUI 在另一台日常很少用的计算机上安装了 .NET 框架。(尽管博士想过当一个冒险者,在他的膝上型电脑上安装。)

获得 .NET 的方式

 最容易获得 Visual Studio.NET 和 .NET 框架SDK 的地方是 MSDN .NET 开发人员中心 (http://msdn.microsoft.com/net/)。

如果仅仅是想要签出 .NET 框架,且具有很快的连接,那么您可以免费下载它(很大,大概 111 MB)。您也可以免费定购它,但您需要负担 CD 的邮寄费用。.NET 框架SDK 包含编译器(甚至 C#!),但不包括集成的开发环境 (IDE)。

但是如果您想要定购 .NET 框架SDK 的 CD,请定购整个 Visual Studio.NET CD 集(其中包含三张 CD,您还是只需负担邮寄费用)。如果您是 MSDN 全球订户,则只能下载 Visual Studio.NET,它的大小约为 1.5 GB,所以必须有真正快速的 Internet 连接。

安装

 绝大部分安装都简单易行,只需单击适当的安装程序或 URL 就可以了。安装程序易于理解而又方便。

不过,还是有一个小技巧:安装 .NET 框架SDK 时,系统将询问您是否注册环境变量,以便可以从命令行生成。请确保选择了这个复选框来注册环境变量,这样才能使用命令行工具。

编写 .NET 程序

 Visual Studio.NET 的设计目的是使 .NET 编程变得更简单,除此之外,诸如自动完成语句和拖放窗体编辑之类的功能,使得使用不熟悉的 API 集变得极其简单。

我们将要使用 Visual Studio,但是现在博士要求大家用旧的方式做事:在记事本(或 Visual Studio,如果愿意)中编写源文件,将其保存到磁盘上,然后编译并从命令行运行。这些工作只需要 .NET SDK,而不需要 Visual Studio.NET。使用命令行工具也可以使我们有机会看到产生了什么文件和熟悉某些命令行工具。

注意,因为从 Beta 1(当前版本)到最终上市的版本计划会对 .NET API 做一些重要的改动。所以,您编写的 Beta 1 版代码必须要为在 Beta 2 或最终版本下运行而进行一些修改。但是,现在开始学习的重要概念是不会改变的,所以现在的学习并非徒劳无益。

如何拼写

 在 .NET 程序中使用大小写的惯例也许和您所用过的其他语言不同。但是,.NET 的规则极其简单:所有非参数名的标识符和私有域的名称,其每个词的首字母大写,包括第一个。(Microsoft 建议将所有域都设为私有,但是它们也许要通过一个属性公开。)对于参数名称和私有域的名称,其每个词的首字母大写,第一个除外。您必须了解这些规则,因为 .NET 框架名称遵循这些规则,并且某些语言(如 C#)是区分大小写的。

所以,主要的输出函数名为 System.Console.WriteLine,严格遵循大小写规则。System.Console.WriteLine 的格式字符串参数名为 formatString,而不是 FormatString。但是字符串长度属性的名称是 StrLen,而不是 strLen。

对于 Dr. GUI,最困难的事是要记住使用 Main 而不是 main。

Hello World.NET

 现在,没有问题了,这里是 .NET 最简单的“Hello World”:

class MyApp {
public static void Main() {
System.Console.WriteLine("Hello, world! (1)");
}
}

类中的所有对象

 您首先注意到的可能是 Main 函数是类的成员。在 .NET 中,所有方法必须是类或结构的成员。(下次,我们将详细讨论结构。)这是因为在 .NET 中,组织的基本单位是类型(即类或结构)。Visual C++.NET 的托管 C++ 扩展允许您可以在类之外公开地编写变量和函数,但在类中也可以编写变量和函数:隐含的全局类。

看!没有头文件!
不但是类中的所有对象,而且类声明都完成了。除了 C#(和大多数 .NET 语言)源代码的源以外,没有头文件、IDL 文件或其他任何文件。所需要说明类的东西就是类本身。

那么,如果您使用不在源文件中的其他类,会发生什么情况呢?编译器如何明白这个类中的内容?

答案是编译器读取包含您所使用的类的程序集中的元数据。程序仅仅隐式地使用 System.Object(因为所有对象来自于 System.Object)。C# 编译器自动转到 mscorlib.dll 程序集以从中获得元数据,所以它能够获得需要用于 System.Object 的信息。如果使用不同程序集中的类,请在 C# 编译器 (CSC) 命令行中使用 /r 选项以指定程序集,例如:

csc /r foo.dll hello.cs

顺便提一句,模块是一个可执行文件,即应用程序或库。它的扩展名通常是 .exe 或 .dll。程序集是一个或多个模块的集合。程序集中的一个文件具有程序集的清单,或该程序集中文件的列表。程序集是可以被部署和版本化的可执行代码的最小单位(当以后我们查看程序集的清单时,您会注意到只有程序集有版本号)。

Main 很特殊

 下一步您可能会注意到在 Main: public 和 static 上的修饰符。对 C# 中的成员的缺省访问控制是 private,因此我们需要声明 Main: public 以使它可以在运行时被调用。Main 是 static,也可以说是类方法,而不是实例方法。因此运行时可以调用它而不用关心创建 MyApp 对象并将该对象的 this 指针传递给 Main 对象。注意,这里我调用了 MyApp,您可以调用类中的任何对象。这个版本的 Main 不带任何参数并且不返回任何东西,由 void 的返回类型表示。(可以编写带有其他参数的 Main 并返回值)。

最后,我们调用 WriteLine 方法编写字符串。我已经选择使用函数 System.Console.WriteLine 的完全限定名。WriteLine 是一个 System 名称空间中的 Console 类的 static 方法(和 Main 一样)。

减少键入

 经常输入完全限定的类名是很麻烦的事情,所以 C# 允许您指定用 using 语句查找的名称空间。例如:

using System;
class MyApp {
public static void Main() {
Console.WriteLine("Hello, world! (2)");
}
}

因为编译器搜索 System 名称空间和 local 名称空间,所以我们不必在 Console.WriteLine 的调用中指定 System。这并不是一个大的胜利,但是当您有许多对象引用和方法调用时,它非常方便。在下面的程序中您会看到这一点。

等一下!别走开!

 在某些情况下(例如从文件管理器中运行 hello.exe),命令窗口可能会在您看到输出之前消失。要防止发生这种情况,请调用 Console 的 ReadLine 方法从键盘上读取,这将导致程序在您按 ENTER 键之前暂停。(我们将始终将它作为控制台应用程序的 Main 中的最后一个语句。)

程序如下所示:

using System;
class MyApp {
public static void Main() {
Console.WriteLine("Hello, world! (3)");
Console.ReadLine(); // 等待直到按 Enter 键;最后一行
}
}

看到了吗?您可以在 C# 中交换!

 正如博士所提到的,与其他一些语言不同,您可以在 C# 中编写交换两个变量的函数。

这是一个交换两个整型变量内容的 C# 程序:

using System;
class SwapInts {
public static void Main() {
Int32 i = 1, j = 2;
Console.WriteLine("i 是 {0}, j 是 {1}", i, j);
Swap(ref i, ref j);
Console.WriteLine("i 是 {0}, j 是 {1}", i, j);
Console.WriteLine(
"i 仍是 {1:####},j 仍是 {0:####}",
j, i
);
Console.ReadLine();
}

static void Swap(ref Int32 a, ref Int32 b) {
Int32 t = a;
a = b;
b = t;
}
}

这里要注意一些事情。第一,博士已经选择使用 .NET 框架类型 System.Int32(Int32 的全名)而不是 C# 类型 int。因为 C# 中的 int 只是 System.Int32 的一个简单别名,此外没有任何差别。但是,Dr. GUI 希望使用 .NET 框架类型而不是特定于 C# 的类型,这使得将示例转换为 C# 以外的语言要容易一点。这些类型在所有语言中都是一致的。(当 Dr. GUI 用 C# 为 C# 程序员编写代码时,他趋向于使用 C# 类型,因为它们使他想起 C/C++ 类型,并且感觉很好。)

下一步,注意 Console.WriteLine 所提供的灵活的格式。变量被插入到输出字符串中,在括号中表达式 {0} 和 {1} 的位置。数字 0 和 1 代表将要被格式化的参数的位置。第一个参数编号为零而不是一。(数组编号也是从零开始的,这就是为什么这个专栏叫做 GUI.NET #0,而不是 #1)。您会注意到博士在最后的 WriteLine 语句中切换了变量。将格式字符串转换到具有不同文法的语言时,能够切换次序是非常重要的,即您可以改变格式字符串而不用改变调用。另外,还可以在括号中加入其他信息,正如在最后一条 WriteLine 语句中。这种格式化机制对于类型来说也是可扩充的。

最后,请注意最重要的一点:在 Swap 的调用和函数声明中,通过引用传递的参数用 ref 来标记。这就告诉了 C# 编译器我们想要在调用方法时将交换 i 和 j 的方式。如果不使用 ref 标记,则要交换由值传递值时生成的副本(您可以试一下删除 ref 的全部四个实例,然后亲自查看结果)。

实践与探讨
 
 让我们动手吧!

 一旦安装了 .NET,您就可以使用记事本或 Visual Studio 创建包含前面的“Hello World”程序之一的文件。创建一个空的子目录,将文件保存到该子目录中,并命名为 hello.cs(CS 表示为 C# 文件)。

下一步,打开命令提示并更改到正在使用的目录。键入“csc hello.cs”命令来编译该程序。如果在目录中查看,您将发现已经创建了 hello.exe 文件。键入“hello”执行该文件,控制台窗口将显示“Hello, World!”。

可执行文件的内容

 列出目录的内容,请注意只有两个文件:hello.cs 和 hello.exe。Hello.exe 是一个非常小的文件,只有 3 KB 左右。真简单。

但是,hello.exe 文件中有些什么?这就不那么简单了。有一个存根加载器,即很小的本机机器语言程序,它可加载 .NET Runtime 并给予其控制权以便使它开始执行您的程序。

但是两个重要的部分是元数据和 IL 中的代码,元数据向 Common Language Runtime 和可能引用程序中的类型的编译器解释该程序。(您可能会想起 IL 是 .NET Runtime 的机器语言。)当 Runtime 即将开始执行每种方法时,它将 IL 转换为本机代码,然后执行本机代码。.NET 从来不解释 IL,它总是将 LI 编译成本机代码。

如何查看元数据和 IL?使用 IL 反汇编 (ILDASM) 工具会使该操作变得非常简单。只要在命令提示下输入“ILDASM hello.exe”(假设 hello.exe 文件在当前目录下)就可以了。您可以看到一个如下显示的窗口。


 图 1:ILDASM 窗口

最初,“hello.exe”之下的目录树显示两个部分:清单和类 MyApp。由于模块 hello.exe 有一个清单,所以我们知道它就是程序集的说明文件。在这种情况下,程序集仅包含一个模块文件 hello.exe。

清单

 双击清单将看到元数据的一小部分,这部分说明了这个程序集(应用程序或组件)中包含的模块(文件)。对于第三个 hello.exe 程序,它看起来如下:

.assembly extern mscorlib {
.originator = (03 68 91 16 D3 A4 AE 33 )
.hash = (52 44 F8 C9 55 1F 54 3F 97 D7 AB AD E2 DF 1D E0
F2 9D 4F BC )
.ver 1:0:2204:21
}
.assembly hello as "hello" {
// --- 下面的自定义属性是自动添加的
// 不要取消注释 -------
// .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(bool,
bool) = ( 01 00 00 01 00 00 )
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module hello.exe
// MVID: {59365AA0-B469-4359-9BAF-4840A9257C61}

这个元数据声明程序集 hello 的版本为 0.0.0.0,包含模块 hello.exe,并且使用 1.0.2204.21 版本的 mscorlib 程序集(.NET Runtime 主 DLL)。模块版本标识符 (MVID) 是一个包含此版本模块 hello.exe 的 ID 的全局唯一标识符 (GUID)。

模块元数据

 如果按下 Ctrl+M 组合键,您将看到更多的元数据,这次是模块的元数据,包括每个类和方法:

Version of runtime against which the binary is built : 1.0.2204.21
ScopeName : hello.exe
MVID : {59365AA0-B469-4359-9BAF-4840A9257C61}
// ... stuff removed
TypeDef #1
-------------------------------------------------------
TypDefName: MyApp (02000002)
Flags : [NotPublic] [AutoLayout] [Class] [AnsiClass] (00000000)
Version : 0:0:0:0
Extends : 01000001 [TypeRef] System.Object
Method #1 [ENTRYPOINT]
-------------------------------------------------------
MethodName: Main (06000001)
Flags : [Public] [Static] [HideBySig] [ReuseSlot] (00000096)
RVA : 0x00002050
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
ReturnType: Void
No arguments.
Method #2
-------------------------------------------------------
MethodName: .ctor (06000002)
Flags : [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor] (00001886)
RVA : 0x00002062
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
ReturnType: Void
No arguments.
// ... REMOVED TypeRef #1 (System.Object .ctor)
// ... REMOVED TypeRef #2
// (System.Diagnostics.DebuggableAttribute .ctor)
TypeRef #3 (01000003)
-------------------------------------------------------
Token: 0x01000003
ResolutionScope: 0x23000001
TypeRefName: System.Console
MemberRef #1
-------------------------------------------------------
Member: (0a000002) WriteLine:
CallCnvntn: [DEFAULT]
ReturnType: Void
1 Arguments
Argument #1: String
MemberRef #2
-------------------------------------------------------
Member: (0a000003) ReadLine:
CallCnvntn: [DEFAULT]
ReturnType: String
No arguments.
// ... REMOVED Assembly metadata; similar to Manifest above
// ... REMOVED AssemblyRef #1 metadata; similar to Manifest above
User Strings
-------------------------------------------------------
70000001 : (17) L"Hello, world! (3)"

可以找到 MyApp 的定义 (TypeDef #1) 和两种方法,即 constructor 方法和 Main 方法。(您并没有编写 constructor 方法,它是由 C# 编译器提供的。)还可以看到对象的引用和程序引用的方法,例如 System.Console 及其 WriteLine 和 ReadLine 方法。还有一些是由编译器提供的其他东西,如 System.Object 和 System.Diagnostics.DebuggableAttribute。这些东西已经从文章中删除以使得文章更简短。您可以通过生成 hello.exe 文件来查看这些内容并使用 ILDASM 进行检查。我还删除了程序集和程序集引用元数据,它们大多数都是所示的清单的重复。

现在理解全部这些内容并不重要,但是博士希望您能看一下可执行文件中的元数据,他希望您能看到元数据包含所有 Runtime 和编译器需要的有关程序集及其模块、类型和成员(如方法)的信息。

类元数据

 如果在主 ILDASM 窗口中双击 MyApp 类,您将看到 MyApp 类的两个方法:constructor 和 Main。也可以看到类的元数据。

在这种情况下,它表示(双击它时)MyApp 是从 System.Object 中衍生出来的,System.Object 是 mscorlib 程序集的一部分:

.class private auto ansi MyApp
extends [mscorlib]System.Object
{
} // end of class MyApp

IL 代码

 如果双击 Main,您将看到它的 IL 代码:

.method public hidebysig static void Main() il managed
{
.entrypoint
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldstr "Hello, world! (3)"
IL_0005: call void [mscorlib]System.Console::WriteLine(class System.String)
IL_000a: call class System.String [mscorlib]System.Console::ReadLine()
IL_000f: pop
IL_0010: ret
} // end of method MyApp::Main

注意 IL 非常简单:将指针推到堆栈上的字符串并调用该方法就构成了对 Console.WriteLine 的调用。与此类似,ReadLine 的调用也很简单,但是请注意,调用后返回值从堆栈中弹出(然后就被忽略了)。最后,Main 返回到其调用者。您也可以看到 Main 被声明为入口点。

Constructor 的 IL 同样简单:

.method public hidebysig specialname rtspecialname
instance void .ctor() il managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method MyApp::.ctor

它复制 this 指针,然后调用基础类 (System.Object) 构造程序。

现在进行交换

 下一步,遵循所述的步骤,从“实践和探讨”开始,在以前显示的程序 SwapInts 上创建源文件,编译然后运行。查看元数据并注意关于 Swap 函数的参数信息。还要看一下 Swap 的调用和 Swap 函数的 IL 代码。

在 Main 的 IL 代码中,会看到一些加框指令。因为 Console.WriteLine 将非整数对象作为格式化字符串后面的参数,因此必须将整数转化为对象。非对象的类型到对象的转化称为加框,并且使用加框指令在 IL 中完成。

亲自动手做更多事情

 如果还有额外的时间,请修改这里已经完成的程序并做一些您感兴趣的事。为了帮助您完成这项工作,请使用 .NET 框架SDK 提供的文档。

启动 .NET 框架SDK 概述页面,您可以在其中找到所有需要的内容。.NET 框架SDK 的安装程序在“开始”菜单和桌面上都放置了快捷方式。您尤其需要关注下列章节:

  • C# 示例、程序员指南和参考(在 .NET 框架SDK 文档帮助文件中的 .NET 框架编译器和语言参考下)
  • .NET 框架参考(也在帮助文件中)
  • .NET 框架开发人员指南(还是在帮助文件中)
  • 示例,尤其是快速入门的示例(注意,您必须展开它们,第一次从 .NET 框架SDK 概述页面中选择它们时,它们将自动展开)
  • IL 指令参考(可以在概述页面的“工具开发人员指南”下找到)

在文档中查找所需的资料

 Visual Studio.NET 和 .NET 框架SDK 中包含的文档浩如烟海。安装 .NET 框架SDK 时,在“开始”菜单下的“.NET 框架SDK”上可以找到和这些文档的链接。.NET 安装程序还在桌面上放置概述的图标。概述是最佳的出发位置——它包括通向所有文档的链接,甚至到 IL 指令参考的链接。

其中的许多文档(并不是所有)目前可以在 http://msdn.microsoft.com/library/dotnet/cpguide/cpguide_start.htm 上联机获得。要想得到完整的文档集,至少需要下载或定购 .NET 框架SDK。(例如,《工具开发人员指南》和所有的 C# 文档及示例不能以联机方式获得)。

请花一点时间从头到尾浏览这些文档,以查看其中包含的内容,您将惊奇地发现其中有大量的材料,尽管其中有很多尚待编写。如果没有找到所需的资料,请检查概述页面以查看它是否在与您所预期的不同章节中。

在哪里可学习更多

 如果您真正想要快速掌握 .NET,请阅读一些材料或参加有关的讲座。Dr. GUI 曾参加过《Wintellect》(http://www.wintellect.com/) 的作者 Jeffrey Richter 先生的讲座。Jeff 先生熟知他的材料,并且知道如何清楚地阐释它们。Dr. GUI 从 Jeff 先生的讲座中获益非浅 — 这篇文章就是。(但是,Dr. GUI 承认,对于 Jeff 先生与 .NET Runtime 小组在同一幢办公楼里工作,他有一点嫉妒。)其他公司,如 DevelopMentor (http://www.developmentor.com/),也提供了一些讲座与资源。

在 http://discuss.develop.com/ 上有一组 .NET 邮件列表,在 http://communities.microsoft.com/newsgroups/default.asp?icp=msdn&slcid=us 上有一系列新的 Microsoft 新闻组。DevelopMentor 的邮件列表非常繁忙,但是您可以浏览 Web 上的存档文件,Microsoft 新闻组是新建的,内容并不多——现在正是加入其中投稿的好时机!

最后,别忘了定期访问 MSDN .NET 开发人员中心 (http://msdn.microsoft.com/net/) ,这样才能不断学习有关资源,跟上 .NET 的潮流。

已经做的工作,下一步将要做的工作

 这次我们说了很多关于 .NET 的内容,尤其是关于 .NET 框架和 Runtime,以及为什么要关注它们。我们还介绍了如何获得并安装 .NET 框架SDK 和/或 Visual Studio.NET 的 Beta 1 版,然后在命令行运行一些简单的程序。最后,简略地谈了一下这些程序的元数据和 IL,并花了一点时间浏览文档。

下一次,我们将讨论所有 .NET 类的父类——为人熟知的 System.Object。我们还将讨论内存分配和垃圾回收,以及一些有趣的接口和类型。

 


版权所有:UML软件工程组织