CLS是一套针对编程语言的变成规范,.NET环境对语言没有特定的限定,只要是符合CLS规范的语言,我们就可以说它是和.NET兼容的语言。在实际的项目开发过程中,我们可以使用不同的语言,例如,当我们需要引入一些第三方产品的程序集时,我们不能保证程序集中使用的语言是和我们使用的编程语言是一致的,因此,确保程序集必须是CLS兼容的。
CLS兼容性实际上采用一种“最大公分母”的方式来实现互操作的,要创建CLS兼容的程序集,我们需要遵循两条原则:1)程序集中所有公有和受保护成员所使用的参数和返回值的类型都必须是与CLS兼容的;2)任何CLS不兼容的公有和受保护的成员,都必须有一个与CLS兼容的替代品。
我们可以在程序集中添加CLSCompliant特性来让编译器保证编译出来的程序集是CLS兼容的。
[ assembly: CLSCompliant( true ) ]
在使用了这个特性后,编译器会在编译的时候确保程序集与CLS兼容,如果不兼容,编译器就会报告错误。
上述第二条原则,我们需要确保为程序集的使用者提供一种不依赖于语言的访问方式,同时也要确保那些和CLS不兼容的对象不会经过多态机制隐藏在程序集的接口中。
对于操作符重载来说,并不是所有.NET语言都支持的,因此最好还是以辅助方法的形式实现,而不要重载操作符。
对于接口中的参数,我们也要注意,来看下面的代码。
// Hiding the non-compliant event argument:
public delegate void MyEventHandler(
object sender, EventArgs args );
public event MyEventHandler OnStuffHappens;
internal class BadEventArgs : EventArgs
{
internal UInt32 ErrorCode;
}
// Code to raise Event:
BadEventArgs arg = new BadEventArgs( );
arg.ErrorCode = 24;
// Interface is legal, runtime type is not:
OnStuffHappens( this, arg );
上述代码就以多态的方式使得接口中的参数变得CLS不兼容。
来看下面的代码。
[ assembly:CLSCompliant( true ) ]
public interface IFoo
{
void DoStuff( Int32 arg1, string arg2 );
}
public interface IFoo2
{
// Non-CLS compliant, Unsigned int
void DoStuff( UInt32 arg1, string arg2 );
}
对于上述代码中的Foo接口,我们使用CLSCompliant特性进行装饰,说明这个接口是CLS兼容的。要注意一点,只有接口定义在一个CLS兼容的程序集中时,这个接口才可能是CLS兼容的,仅仅遵循CLS规范进行定义式不足够的,即如果Foo接口不在一个CLS兼容的程序集中时,虽然它的声明中使用的参数和返回值的类型都是CLS兼容的,但是这个接口本身并不是CLS兼容的,我们不应该通过Foo接口引用的方式来调用DoStuff()方法。
而对于上述代码中的Foo2接口,它是一个CLS不兼容的接口,因此如果有一个类显示实现了这个接口,那么这个类就变为是CLS不兼容的。
如果希望程序集是CLS兼容的,那么需要保证以下内容是CLS兼容的:
- 基类
- 公有或者受保护方法/属性的返回值
- 公有活着受保护方法/索引器的参数
- 运行时事件参数
- 声明或者实现的公有接口
CLS兼容性要求我们花费一些时间来从其他语言的角度思考程序的公有借口,我们并不需要将所有的代码都限定在CLS兼容的范围内,只需要在公有接口中避免出现与CLS不兼容的构造就可以了。