1.10 通用语言规范

来源:互联网 发布:不负如来不负卿网络剧 编辑:程序博客网 时间:2024/05/01 20:04

COM允许不同语言创建的对象彼此通讯, CLR继承了所有语言并允许一种语言创建的对象可以作为另一种完全不同的语言中等同的对象. 这种集成是可能的, 因为CLR中的标准类型集合, metadata(字描述类型信息), 以及通用执行环境.

当这种语言集成是奇异的目标时, 问题出现在编程语言彼此之间有着很大的差异. 例如, 一些语言不能区分大小写的字符, 一些语言不能提供无符号整数, 操作符重载, 或者支持变参数个数的函数.

如果你打算创建很容易被其他语言访问的类型, 你要确保你使用的编程语言特性在所有其他的语言中都存在. 为了帮助你做这样的事情, 微软已经定义了通用语言规范(CLS), 气为编译器厂商描述了它们的编译器必须支持的最小子集特性, 目的是让它们的编译器产生的类型与其它CLS兼容的语言写出的组件兼容.

CLR/CTS支持的特性比CLS定义的子集多得多, 所以如果你不关心语言间互操作的问题, 你可以开发出非常丰富的类型, 但是它限制在语言的特性集合中. 特别地, CLS定义了规则, 让外部可见的类型和方法必须遵守, 来让任何CLS兼容的编程语言访问. 注意CLS规则不适用于只在程序集内部访问的代码. 1-6总结了这段表达的思想.

1-6 每个语言提供了CLR/CTS的子集, 但它们是CLS的超集(但是不必是相同的超集)

如图1-6所示, CLR/CTS提供了一组特性, 一些语言实现了CLR/CTS的很大一个子集. 程序员愿意用IL编程, 例如, 能够使用CLR/CTS提供的所有特性. 大多数其他语言, 例如C# , VBFortran, 只为程序员提供CLR/CTS特性的一个子集. CLS定义了所有语言必须支持的最小特性子集.

如果你正在使用一种语言定义一个类型, 你期望这个类型能被任何一种语言使用, 那么你不应该在类型的公共和保护成员中利用CLS没有包含的任何特性, 否则, 你的类型成员可能不能被其他语言编写的代码访问.

在下面的代码中, CLS兼容的类型使用C#定义的, 然而这个类型只有一些非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 error: this method is private

private UInt32 ABC() { return 0; }

}

}

在这些代码中, [assembly: CLSCompliant(true)]属性被应用于程序集. 这个属性告诉编译器确保任何公开暴露的类型不能有任何阻止类型被其他编程语言访问的代码. 当这个代码被编译的时候, C#编译器产生两个警告, 第一个警告是因为方法Abc返回一个无符号整数, 其他一些语言不能操作无符号整数. 第二个警告是因为这个类型暴露两个公有方法, 但是它们只是在名称的大小写和返回类型上不同: Abcabc. VB和其他一些语言不能调用这两个方法.

有意思的是, 如果你打算从sealed class SomeLibraryType前面删除public, 然后重新编译, 两个警告就都消失了. 原因是SomeLibraryType的类型将默认是internal, 因此不会在暴露给程序集之外. 对于完整的CLS规则列表, 请参考.NET Framework SDK文档的”Cross-Language Interoperability”部分.

CLR, 一个类型的每个成员不是一个字段(data)就是一个函数(行为), 这意味着每个编程语言必须能访问字段和调用方法. 某些字段和方法是以特定的方式使用的, 某些则是以通用的方式使用. 为了使编程简单, 语言都典型地提供了额外的抽象来使得编写这些通用的编程模式变得容易. 例如, 语言都暴露了枚举, 数组, 属性, 索引, 委托, 事件, 构造函数, 析构函数, 操作符重载, 转换操作符等概念. 当一个编译器在你的源代码中涉及这些概念中的任意一个时, 它必须翻译这些东西为字段和方法, 使得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

event EventHandler AnEvent;

}

当编译器编译这些代码时, 结果是一个拥有一个字段和函数的类型. 你能很容易地看到这些, 通过使用.NET Framework SDK提供的IL反汇编工具(ILDasm.exe)来检查产生的托管模块, 如图1-7所示.

 

1-7 ILDasm给出了测试类型的字段和函数(metadata中获得)

1-4给出了编程语言的基本设施如何映射到等价的CLR字段和函数.

类型成员

成员类型

等价的编程语言基本设施

AnEvent

字段

事件, 字段的名字是AnEvent, 它的类型是System.EventHandler.

.ctor

函数

Constructor.

Finalize

函数

Finalizer.

add_AnEvent

函数

Event add accessor method.

get_AProperty

函数

Property get accessor method.

get_Item

函数

Indexer get accessor method.

op_Addition

函数

+ operator.

op_Equality

函数

== operator.

op_Inequality

函数

! = operator.

remove_AnEvent

函数

Event remove accessor method.

set_AProperty

函数

Property set accessor method.

set_Item

函数

Indexer set accessor method.

Test类型在表1-4中没有提到的地方是.class, .custom, AnEvent, AProperty, Item, 这些地方不映射到字段或者函数, 他们仅仅提供了一些关于类型的额外信息, 这些信息可以被CLR, 编程语言, 或者工具访问. 例如, 一个工具可以看到Test类型提供一个称为AnEvent的事件, 它通过两个方法暴露出来(add_AnEventremove_AnEvent).

原创粉丝点击