CLR via C# 3 读书笔记(8):第1章 CLR执行模型 — 1.8 通用语言规范

CLR允许不同语言创建的对象之间能够相互进行通信。换句话说,CLR集成了所有语言,并且允许一种语言创建的对象在另一种完全不同的语言编写的代码中被看做是等同的成员。CLR的标准类型集合、元数据(自描述类型信息)和通用执行环境,使这种集成成为可能。

语言继承是个空想,不同语言之间差异太大。例如,有些语言不区分大小写,有些不支持无符号整型,操作符重载,有些方法不支持可变数目的参数。

如果希望创建的类型被其他编程语言轻松地访问,只能使用编程语言中那些对其他语言来说也同样可用的特性。为此,微软定义了通用语言规范(Common Language Specification,CLS),描述了面向CLR的编译器必须支持的最小特性集合。

CLR/CTS所支持的特性比CLS定义的子集要丰富得多。所以如果你不关心多语言互操作,则可以开发出只受所选语言的特性集合限制的丰富的类型。CLS定义了可以被任何与CLS兼容的编程语言访问到的、外部可见的类型和方法所必须遵循的规则。注意CLS规则不适用于仅在所定义的程序集中可访问的代码。

image

如上图所示,CLR/CTS提供了一组特性。一些语言提供了CLR/CTS的较大子集。例如,使用IL汇编语言的程序员可以使用所有CLR/CTS提供的特性。而大多数语言,如C#、VB、Fortran,提供了CLR/CTS特性的一个子集。CLS定义了所有语言必须支持的一个最小的特性集合。

如果你打算采用某种语言设计一个类型,并希望该类型被其他语言使用,那么就不能在public和protected成员中使用任何CLS以外的特性。否则意味着使用其他语言的程序员可能无法访问你的类型成员。

下面的代码用C#定义一个CLS兼容的类型。其中一些与CLS不兼容的构造会导致C#编译器报错。

using System;

// Tell compiler to check for CLS compliance
[assembly: CLSCompliant(true)]

namespace SomeLibrary {
   // Warnings appear because the class is public
   public sealed class SomeLibraryType {

      // Warning: Return type of 'SomeLibrary.SomeLibraryType.Abc()' 
      // is not CLS-compliant
      public UInt32 Abc() { return 0; }

      // Warning: Identifier 'SomeLibrary.SomeLibraryType.abc()' 
      // differing only in case is not CLS-compliant
      public void abc() { }

      // No warning: Method is private
      private UInt32 ABC() { return 0; }
   }
}

在这段代码中,我们在程序集上应用了[assembly:CLSCompliant(true)]特性。该特性告诉编译器必须确保在所有公共暴露的类型中,不包含会阻止其他编程语言访问该类型的构造。当这段代码被编译时,C#编译器生成两个警告。第一个警告是因为方法Abc返回了一个无符号的整数,有些编程语言不能操作无符号整数值。第二个警告是因为该类型暴露的两个公有方法abc与Abc仅存在大小写和返回值的差别,VB等语言不能调用这两个方法。

有趣的是,如果删除sealed class SomeLibraryType前面的public并且重新编译,所有的警告都将消失。这是因为SomeLibraryType的类型默认为internal,不会暴露给外部程序集。

CLS的简化描述如下。在CLR中,类型的每一个成员都是一个字段(数据)或方法(行为)。这意味着么个编程语言都能够访问字段和调用方法。这些字段和方法以或者特殊或者普通的应用方式被展现出来。为了简化编程,编程语言通常会提供额外的抽象,事这些通用的编程模式编写起来更加容易。例如,一些语言提供了枚举、数组、属性、索引器、委托、事件、构造函数、析构器、操作符重载、转换操作符等概念。当编译器在源代码中遇到这些构造时,必须将它们翻译为字段或方法,这样CLR和其他编程语言才能够访问它们。

下面的类型包含了构造函数、析构器、操作符重载、属性、索引器和一个事件。注意这里的代码仅仅为了通过编译,并不是一个类型正确的实现方法。

using System;

internal sealed class Test {
   // Constructor
   public Test() { }

   // Finalizer
   ~Test() { }

   // Operator overload
   public static Boolean operator ==(Test t1, Test t2) {
      return true;
   }
   public static Boolean operator !=(Test t1, Test t2) {
      return false;
   }

   // An operator overload
   public static Test operator +(Test t1, Test t2) { return null; }

   // A property
   public String AProperty {
      get { return null; }
      set { }
   }

   // An indexer
   public String this[Int32 x] {
      get { return null; }
      set { }
   }

   // An event
   public event EventHandler AnEvent;
}

当编译器编译这段代码后,得到的结果是一个包含几个字段和方法的类型。可以通过.NET Framework SDK提供的IL反编译工具(ILDasm.exe)来检查最终的托管模块。

image

下表展示了编程语言的构造到相应的CLR字段和方法的映射:

image

Test类型下面还有一些节点,如.class、.custom、AnEvent、AProperty和Item,它们标识了类型中一些额外的元数据。这些节点没有映射到任何字段和方法,而是仅仅提供了一些类型的附加信息,这些信息可以被CLR、编程语言和一些工具访问到。例如,使用某个工具我们可以发现Test类型提供了一个名为AnEvent的事件,可以通过两个方法访问(add_AnEvent和remove_Event)。

posted @ 2010-03-09 09:17  麒麟.NET  阅读(1828)  评论(5编辑  收藏  举报