首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

11.3  构建和使用单文件程序集

在开始进入.NET程序集世界前,首先创建一个包含几个公共类型的单文件*.dll程序集(名为名为CarLibrary)。使用Visual Studio 2005构建这样一个代码库,只需选择类库(Class Library)项目便可(如图11-5所示)。

11-5  创建一个C#代码库

在这个汽车类库中,首先创建一个名为Car的抽象基类,它通过自定义属性定义了一些受保护的数据成员。它还拥有一个抽象方法TurboBoost(),使用自定义的枚举类型(EngineState)来表示汽车引擎的当前状态。

 

using System;

 

namespace CarLibrary

{

    // 表示引擎的状态。

    public enum EngineState     

    {  engineAlive, engineDead }

 

// 抽象基类。

public abstract class Car   

{

    protected string petName;

    protected short currSpeed;

    protected short maxSpeed;

    protected EngineState egnState = EngineState.engineAlive;

 

    public abstract void TurboBoost();

 

    public Car(){}

    public Car(string name, short max, short curr)

    {

        petName = name; maxSpeed = max; currSpeed = curr;

    }

 

    public string PetName

    {

        get {  return petName; }

        set {  petName = value; }

    }

    public short CurrSpeed

    {

        get {  return currSpeed; }

        set {  currSpeed = value; }

    }

    public short MaxSpeed

    {  get {  return maxSpeed; }  }

    public EngineState EngineState

    {  get {  return egnState; }  }

    }

}

 

现在假设Car类型有两个子类:MiniVan(旅行车)SportsCar(跑车)。每个子类都以一定方式重写抽象方法TurboBoost()

 

using System;

using System.Windows.Forms;    

 

namespace CarLibrary

{

    public class SportsCar : Car

    {

        public SportsCar(){ }

        public SportsCar(string name, short max, short curr)

             : base (name, max, curr){ }

 

        public override void TurboBoost()

        {

             MessageBox.Show("Ramming speed!", "Faster is better...");

        }

    }

   

    public class MiniVan : Car

    {

        public MiniVan(){ }

        public MiniVan(string name, short max, short curr)

             : base (name, max, curr){ }

        public override void TurboBoost()

        {

 

             // Minivans引擎的马力不足。

             egnState = EngineState.engineDead;

             MessageBox.Show("Time to call AAA", "Your car is dead");

        }

    }

}

 

请注意,每一个子类实现TurboBoost()时都使用了MessageBox类,该类定义在System.Windows. Forms.dll程序集中。为使程序集使用在外部程序集中定义的类型,因此需要通过Add Reference对话框来为CarLibrary项目添加对外部二进制文件的引用(如图11-6所示)。Add Reference对话框可以通过在Visual Studio 2005中选择ProjectAdd Reference打开。

11-6  在这里开始引用外部.NET程序集

这里必须清楚的一点是:Add-Reference对话框的.NET选项卡并没有显示机器上所有的程序集。Add Reference对话框并不会显示我们自定义的程序集,也不会显示所有部署在GAC(全局程序集缓存)中的程序集。它只显示Visual Studio 2005预先设定的一组常用的程序集。当构建的应用程序需要添加一个在Add Reference对话框中未列出的程序集时,我们需要单击Browse选项卡手动找到该*.dll*.exe

注解    事实上我们可以把程序集复制到C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\ PublicAssemblies这样Add Reference对话框就会显示自定义的程序集。但是这样做的好处并不大,因为“Recent”选项卡会不断更新显示最近被引用的程序集。

11.3.1  清单

文本框:  
图11-7  使用ildasm.exe打开CarLibrary.dll

在客户端应用程序使用CarLibrary.dll之前,先查看一下代码库的内部结构。假定项目已经编译完毕,现在使用ildasm.exe打开CarLibrary.dll(如图11-7所示)。

双击MANIFEST图标打开CarLibrary.dll的清单。该清单的第一个代码块描述了当前程序集正确运行所必需的所有外部程序集。你应记得,CarLibrary.dll使用了mscorlib.dllSystem. Windows.Forms.dll里面的类型,因此两者都在清单中通过.assembly外部标记列出。

 

.assembly extern mscorlib

{

.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )

.ver 2:0:0:0

}

.assembly extern System.Windows.Forms

{

.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )

.ver 2:0:0:0

}

 

每一个.assembly外部块由.publickeytoken指令和.ver指令组成。.publickeytoken指令仅当程序集被配置为强名称(本章后面会详细介绍)的时候才需要。而.ver指令则用于表示数字版本标识。

在这些外部引用信息之后,我们会发现很多.custom标记,它们标识了程序集级别的特性(attribute)。如果查看由Visual Studio 2005创建的AssemblyInfo.cs文件,会发现这些特性表示程序集的各种基本特征,如公司名称、商标等(所有这些属性目前为空)。第14章会详细介绍特性,因此这里我们先放一放。但读者必须知道,定义在AssemblyInfo.cs文件里面的特性会用各种.custom标记(如[AssemblyTitle]特性)来更新清单:

 

.assembly CarLibrary

{

...

  .custom instance void [mscorlib]

  System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 00 00 00 )

  .hash algorithm 0x00008004

  .ver 1:0:454:30104

}

.module CarLibrary.dll

 

最后,在上面的代码块中可能发现,.assembly标记用于标识程序集的友好名称(CarLibrary),而.module标记用于标识模块自身的名称(CarLibrary.dll)。.ver标记则定义了程序集被赋予的版本号,这个版本号由AssemblyInfo.cs文件中的[AssemblyVersion]特性决定。本章后面将会详细介绍程序集的版本化,在这里先说一点:[AssemblyVersion]特性里面的*通配符用于通知Visual Studio 2005在每次编译程序集时,递增更新生成版本号(build)和修订版本号(revision)。

11.3.2  CIL

前面介绍过,程序集并不包含特定平台的指令。相反,它包含的是独立于平台的CIL代码。当.NET运行库把一个程序集加载进内存时,CIL将会被(JIT编译器)编译成目标平台可以理解的指令。如果双击SportsCar类型的TurboBoost()方法,ildasm.exe会打开一个新窗口展示CIL指令:

 

.method public hidebysig virtual instance void

    TurboBoost() cil   managed

{

    // 代码大小             170X11

  .maxstack  2

  IL_0000:  ldstr  "Ramming speed!"

  IL_0005:  ldstr  "Faster is better..."

  IL_000a:  call valuetype [System.Windows.Forms]

    System.Windows.Forms.DialogResult [System.Windows.Forms]

    System.Windows.Forms.MessageBox::Show(string, string)

  IL_000f:  pop

  IL_0010:  ret

} // 方法SportsCar::TurboBoost结束

 

可以看出,.method标签用于标识SportsCar类型中定义的方法,.field标签则用于标识该类型中定义的成员变量。回想一下,前面Car类定义了一组受保护的数据成员,如currSpeed:

 

.field family int16 currSpeed

 

.property标签标识了属性。下面是描述公共属性CurrSpeedCIL代码(注意,属性的读/写特性分别使用.get标签和.set标签标识):

 

.property instance int16 CurrSpeed()

{

  .get instance int16 CarLibrary.Car::get_CurrSpeed()

  .set instance void CarLibrary.Car::set_CurrSpeed(int16)

} // 属性Car::CurrSpeed的结束。

 

文本框:  
图11-8  CarLibrary.dll中的类型元数据


11.3.3  类型元数据

最后,如果按下Ctrl+M组合键,ildasm.exe会显示CarLibrary.dll程序集中的每一个类型的元数据(如图11-8所示)。

现在已经对CarLibrary.dll程序集的内部结构有一定的了解,接下来开始构建使用该程序集的客户端应用程序。

源代码      CarLibrary项目的源代码位于Chapter 11目录下。

11.3.4  构建C#客户端应用程序

由于CarLibrary中的每一个类型都被声明为公共的(public),因此其他程序集都可以使用它们。其实可以使用internal关键字把类型定义为内部的(如果不具体定义某个类型为publicC#默认的访问方式是internal)。内部类型只能被它所在的程序集使用,而不能被外部客户端看到和创建。

注解   目前.NET 2.0提供一种友元程序集机制它允许内部类型被特定的程序集所使用。请在.NET Framework 2.0 SDK文档中查找InternalsVisibleToAttribute以获取更多信息。

为了能够调用外部类型,我们创建一个C#控制台应用程序项目(名为名为CSharpCarClient),然后使用Add Reference对话框的Browse选项卡添加对CarLibrary.dll的引用(如果使用Visual Studio 2005编译CarLibrary.dll,程序集将被放到CarLibrary项目文件夹的子目录\Bin\Debug下)。一旦点击了OK按钮,Visual Studio 2005将会把CarLibrary.dll复制到CSharpCarClient项目文件夹的子目录\Bin\Debug下(如图11-9所示)。

11-9  Visual Studio 2005将私有程序集复制到客户端的目录下

现在,我们可以构建客户端应用程序去使用这些外部类型了。请把先前的C#文件做如下更改:

 

// 不要忘记'use'(使用)命名空间CarLibrary

using CarLibrary;

 

namespace CSharpCarClient

{

  public class CarClient

  {

    static void Main(string[] args)

    {

             // 创建Sports Car对象。

      SportsCar viper = new SportsCar("Viper", 240, 40);

      viper.TurboBoost();

 

             // 创建MiniVan对象。

      MiniVan mv = new MiniVan();

      mv.TurboBoost();

      Console.ReadLine();

    }

  }

}

 

以上代码跟先前编写的其他应用程序看上去没有什么区别。唯一的不同是这个C#客户端应用程序使用定义在另一个自定义程序集里面的类型。现在运行程序,它会如你所愿打开多个不同的对话框。

源代码      CSharpCarClient项目的源代码位于Chapter 11目录下。

11.3.5  构建Visual Basic .NET客户端应用程序

为了阐明.NET平台的语言无关性,让我们使用另外一种.NET编程语言Visual Basic .NET来构建新的控制台应用程序(VbNetCarClient)。如图11-10所示,先创建一个项目,然后使用Add Reference对话框添加对CarLibrary.dll的引用。

11-10  创建Visual Basic .NET控制台应用程序

C#一样,Visual Basic .NET要求列出当前文件使用的命名空间。但是Visual Basic.NET使用Imports关键字而不是C#using关键字。因此,在Module1.vb代码文件中添加以下Imports语句:

 

Imports CarLibrary

 

Module Module1

 

  Sub Main()

  End Sub

 

End Module

 

注意,Main()方法定义在Visual Basic .NET Module类型(跟多文件程序集中的*.netmodule文件没有关系)中。Module类型是Visual Basic .NET对只含有静态方法的密封类的简写方式。为了说明问题,请看以下等价的C#代码:

 

// VB.NET'Module'其实是一个密封类,包含若干静态方法。

public sealed class Module1

{

  public static void Main()

  {

  }

}

 

为了在Visual Basic .NET下使用MiniVanSportsCar类型,把Main()方法改写为:

 

Sub Main()

  Console.WriteLine("***** Fun with Visual Basic .NET *****")

  Dim myMiniVan As New MiniVan()

  myMiniVan.TurboBoost()

 

  Dim mySportsCar As New SportsCar()

  mySportsCar.TurboBoost()

  Console.ReadLine()

End Sub

 

编译后,运行本应用程序,将再次看到程序打开了一连串的对话框。

11.3.6  实现跨语言继承

使用.NET开发最吸引人的一个方面就是跨语言继承。为了说明这一点,我们创建一个新的Visual Basic .NET类,用它继承(之前使用C#编写的)SportsCar。首先,(通过选择ProiectAdd Class)为当前Visual Basic .NET应用程序添加一个新的类文件,命名为PerformanceCar.vb。使用Inherits关键字更改该类的定义,让它继承SportsCar类型。然后,使用Overrides关键字重写抽象方法TurboBoost()

 

Imports CarLibrary

 

'这个VB类型继承了用C#编写的SportsCar类型。

Public Class PerformanceCar

  Inherits SportsCar

    Public Overrides Sub TurboBoost()

      Console.WriteLine("Zero to 60 in a cool 4.8 seconds...")   

    End Sub

End Class

 

为了测试新类类型,更新ModuleMain()方法,如下所示:

 

Sub Main()

...

  Dim dreamCar As New PerformanceCar()

 

    '继承的属性。

  dreamCar.PetName = "Hank"

  dreamCar.TurboBoost()

  Console.ReadLine()

End Sub

 

无论基类是用完全不同的语言编写,还是定义在完全不同的代码库中,dreamCar对象都可以调用其继承链上任何的公共成员(比如PetName属性)。

源代码      VbNetCarClient项目的源代码位于Chapter 11目录下。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论