Effective C#之Item 30:Prefer CLS-Compliant Assemblies

  rel="File-List" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_filelist.xml"> rel="themeData" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_themedata.thmx"> rel="colorSchemeMapping" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_colorschememapping.xml"> Item 30: Prefer CLS-Compliant Assemblies

优先选择与CLS兼容的程序集

The .NET environment is language agnostic: Developers can incorporate components written in different .NET languages without limitations. In practice, it's almost true. You must create assemblies that are compliant with the Common Language Subsystem (CLS) to guarantee that developers writing programs in other languages can use your components.

.Net环境是与语言无关的:开发者可以无限制的合并用其他.Net语言编写的组件。实际上往往也是这样的。你必须创建与通用语言子系统(CLS)相兼容的程序集来保证用其他语言编程的开发者可以使用你的组件。

CLS compliance is a new twist on that least common denominator approach to interoperability. The CLS specification is a subset of operations that every language must support. To create a CLS-compliant assembly, you must create an assembly whose public interface is limited to those features in the CLS specification. Then any language supporting the CLS specification must be capable of using the component. This does not mean you must limit your entire programming palette to the CLS-compliant subset of the C# language, however.

CLS兼容性与互操作性之间至少要有共同特性,CLS规范是一个每个语言都必须支持的操作的集合。你创建的程序集为了与CLS兼容,应该做到:公共接口应该限制在CLS规范的范围之内。那样的话,任何支持CLS规范的语言都具有了使用这个组件的能力。然而,这并不是说,要将个程序限制在与CLS兼容的C#语言特性范围内。

To create a CLS-compliant assembly, you must follow two rules. First, the type of all parameters and return values from public and protected members must be CLS compliant. Second, any non-CLS-compliant public or protected member must have a CLS-compliant synonym.

为了创建与CLS兼容的程序集,应该遵循2个规则。首先,所有参数和公共以及保护成员的返回值应该是CLS兼容的。其次,任何非CLS兼容的公共以及保护成员应该有一个CLS兼容的同义对象。

The first rule is simple to follow: You can have it enforced by the compiler. Add the CLSCompliant attribute to your assembly:

第一条规则很容易遵守:可以通过编译器来强制完成,向程序集添加CLSCompliant特性就可以了:

[ assembly: CLSCompliant( true ) ]

The compiler enforces CLS compliance for the entire assembly. If you write a public method or property that uses a construct that is not compliant with CLS, it's an error. That's good because it makes CLS compliance an easy goal. After turning on CLS compliance, these two definitions won't compile because unsigned integers are not compliant with CLS:

编译器为整个程序集强制保证CLS兼容性。如果你使用一个和CLS不兼容的结构来编写公共方法或者属性,编译器就会报错。这样,使得与CLS兼容变得很容易,是个好办法。在打开CLS兼容选项后,下面2个定义将不能编译通过,因为无符号整型与CLS不兼容:

  1.     // Not CLS Compliant, returns unsigned int:
  2.     public UInt32 Foo()
  3.     {
  4.         return foo;
  5.     }
  6.     // Not CLS compliant, parameter is an unsigned int.
  7.     public void Foo2(UInt32 parm)
  8.     {
  9.     }

Remember that creating a CLS-compliant assembly affects only items that can be seen outside of the current assembly. Foo and Foo2 generate CLS compliance errors when declared either public or protected. However, if Foo and Foo2 were internal, or private, they could be included in a CLS-compliant assembly; CLS-compliant interfaces are required only for items that are exposed outside the assembly.

记住,创建CLS兼容的程序集,仅仅影响从当前程序集外部能看到的那些项目。当Foo Foo2被声明为公共或者保护方法的时候,都会产生CLS兼容错误。然而,如果Foo Foo2internal或者private的,它们就可以被包含在CLS兼容的程序集里面了;CLS兼容性接口,仅仅对于暴露给程序集外部的那些条款有要求。

What about this property? Is it CLS compliant?

属性呢?是CLS兼容的么?

  1.     public MyClass TheProperty
  2.     {
  3.         get { return myClassVar; }
  4.         set { myClassVar = value; }
  5.     }

It depends. If MyClass is CLS compliant and indicates that it is CLS compliant, this property is CLS compliant. On the other hand, if MyClass is not marked as CLS compliant, this property is not CLS compliant. That means that the earlier TheProperty is CLS compliant only if MyClass resides in a CLS-compliant assembly.

这是不确定的。如果MyClassCLS兼容的并且表明自己是CLS兼容的,那么该属性就是CLS兼容的。如果MyClass没有表明自己是CLS兼容的,那么该属性也就不是CLS兼容的。那意味着,只有当MyClass处于一个CLS兼容的程序集中时,前面的TheProperty才是CLS兼容的。

You cannot build a CLS-compliant assembly if you have types in your public or protected interface that are not CLS compliant. If, as a component designer, you do not have an assembly marked as CLS compliant, you make it harder for users of your component to create CLS-compliant assemblies. They must hide your types and mirror the functionality in a CLS-compliant wrapper. Yes, this can be done. But, no, it's not a good way to treat the programmers who want to use your components. It's better to strive for CLS-compliant assemblies in all your work: This is the easiest way for clients to incorporate your work in their CLS-compliant assemblies.

如果在你的公共或者保护接口上有CLS不兼容的类型的话,你就不能构建一个CLS兼容的程序集。作为一个组件设计者,如果你的程序集没有标记为CLS兼容,那么对于你的组件的用户来说,想使用它构建CLS兼容的程序集就很困难了。他们必须隐藏你的类型,在CLS兼容的包装器里面进行方法映射。是的,可以这么做来完成要求。但是,这样对待使用你的组件的程序员,不是一个好方法。最好在你的所有工作上都争取做到CLS兼容:对于客户来说,在CLS兼用的程序集里面利用你的组件进行工作,这样是最简单的方法。

The second rule is up to you: You need to make sure that you provide a language-agnostic way to perform all public and protected operations. You also need to make sure that you do not sneak a noncompliant object through your interface using polymorphism.

第二个规则取决于你:你需要保证能提供一个语言无关的方法来执行所有的公共和保护操作。同时,也需要保证,在你的多态接口上,没有隐藏非兼容的对象。

Operator overloading is a feature that some love and others hate. As such, not every language supports or allows operator overloading. The CLS standard does not take a pro or con stance on the concept of operator overloading. Instead, it defines a function name for each operator: op_equals is the function name created when you write an operator = function. op_addis the name for an overloaded addition operator. When you write an overloaded operator, the operator syntax can be used in languages that support overloaded operators. Developers using a language that does not support operator overloading must use the op_ function name. If you expect these programmers to use your CLS-compliant assembly, you should provide a more convenient syntax. That leads to this simple recommendation: Anytime you overload an operator, create a semantically equivalent function:

操作符重载是有人爱有人恨的一个特性。同样,不是所有的语言都支持操作符重载。CLS标准对于操作符重载没有明确的支持或者反对。相反,它为每个操作符定义了一个方法名:当你写=操作符方法的时候,op_equals就是对应的方法名;当你重载加操作符的时候,op_addis就是方法名。当你编写重载操作符时,操作符语法就可以在支持操作符重载的语言里面使用。使用不支持操作符重载的语言的开发者必须使用op_这种形式的方法名。如果你期望这些程序员使用你的CLS兼容程序集,你就应该提供更方便的语法。这引出了最简单的建议:任何时候当你重载操作符时,都要创建一个基于语义的等价方法名:

  1. // Overloaded Addition operator, preferred C# syntax:
  2. public static Foo operator+( Foo left, Foo right)
  3. {
  4.   // Use the same implementation as the Add method:
  5.   return Foo.Add( left, right );
  6. }
  7.  
  8. // Static function, desirable for some languages:
  9. public static Foo Add( Foo left, Foo right)
  10. {
  11.   return new Foo ( left.Bar + right.Bar );
  12. }

Finally, watch out for non-CLS types sneaking into an interface when you use polymorphic arguments. It's easy to do with event arguments. You can create a type that is not compliant with CLS and use it where a base type that is CLS-compliant is expected.

最后,注意当使用多态参数时,要防止非CLS类型隐藏在接口中。在事件参数中很容易出现这种情况。你可以创建与CLS不兼容的类型,在CLS兼容的基类应该被使用的地方使用它。

Suppose that you created this class derived from EventArgs:

假设你创建了从EventArgs派生的一个类:

 

  1. internal class BadEventArgs : EventArgs
  2. {
  3.   internal UInt32 ErrorCode;
  4. }
  5.  

The BadEventArgs type is not CLS compliant; you should not use it with event handlers written in other languages. But polymorphism makes this easy to do. You can declare the event type to use the base class, EventArgs:

BadEventArgs类型不是CLS兼容的,你不应该将它用做其他语言里的事件处理者。但是多态使得这很容易。你可以宣布事件类型使用基类,EventArgs

  1. // Hiding the non-compliant event argument:
  2. public delegate void MyEventHandler(
  3.   object sender, EventArgs args );
  4.  
  5. public event MyEventHandler OnStuffHappens;
  6.  
  7. // Code to raise Event:
  8. BadEventArgs arg = new BadEventArgs( );
  9. arg.ErrorCode = 24;
  10.  
  11. // Interface is legal, runtime type is not:
  12. OnStuffHappens( this, arg );
  13.  

The interface declaration, which uses an EventArgs argument, is CLS compliant. However, the actual type you substituted in the event arguments was not. The end result is a type that some languages cannot use.

接口声明,使用EventArgs作为参数,是CLS兼容的。然而,实际上在事件参数上替换掉的类型不是CLS兼容的。最终结果是一些语言不能使用。

This discussion of CLS compliance ends with how CLS-compliant classes implement compliant or noncompliant interfaces. It can get complicated, but we'll simplify it. Understanding CLS compliance with interfaces also will help you fully understand what it means to be CLS compliant and how the environment views compliance.

CLS兼容性讨论以“CLS兼容类如何实现兼容或者不兼容的接口”来结束。这可能变得复杂,但是我们来简化它。理解CLS与接口的兼容同时也帮助你完全理解CLS兼容的意义,以及理解环境如何对待兼容性。

This interface is CLS compliant if it is declared in a CLS-compliant assembly:

这个接口,如果在一个CLS兼容的程序集里面被声明,那么它就是CLS兼容的:

  1. [ assembly:CLSCompliant( true ) ]
  2. public interface IFoo
  3. {
  4.   void DoStuff( Int32 arg1, string arg2 );
  5. }

You can implement that interface in any CLS-compliant class. However, if you declare this interface in an assembly that is not marked as CLS compliant, the IFoo interface is not CLS compliant. In other words, an interface is CLS compliant only if it is defined in a CLS-compliant assembly; conforming to the CLS spec is not enough. The reason is compiler performance. The compilers check CLS compliance on types only when the assembly being compiled is marked as CLS compliant. Similarly, the compilers assume that types declared in assemblies that are not CLS compliant actually are not CLS compliant. However, the members of this interface have CLS-compliant signatures. Even if IFoo is not marked as CLS compliant, you can implement IFoo in a CLS-compliant class. Clients of this class could access DoStuff through the class reference, but not through the IFoo reference.

你可以在任何CLS兼容的类里面实现该接口。然而,如果你在一个没有标记为CLS兼容的程序集里面声明这个接口,那么IFoo接口就不是CLS兼容的。换句话经,对于一个接口,仅仅当它在一个CLS兼容的程序集里面被定义的时候,它才是CLS兼容的;仅仅遵循CLS规范是不够的。原因就是编译器性能。仅仅当正被编译的程序集被标注为CLS兼容时,编译器才在类型上检查CLS兼容性。类似的,编译器假设:在CLS不兼容的程序集里面声明的类型是CLS不兼容的。然而,该接口的成员有CLS兼容签名。即使假如IFoo没有被标记为CLS兼容,你也可以在一个CLS兼容的类里面实现IFoo。该类的用户,可以通过类的引用访问DoStuff,而不是通过IFoo引用。

Consider this small variation:

考虑这个小小的变化:

  1. public interface IFoo2
  2. {
  3.   // Non-CLS compliant, Unsigned int
  4.   void DoStuff( UInt32 arg1, string arg2 );
  5. }

A class that publicly implements IFoo2 is not CLS compliant. To make a CLS-compliant class that implements IFoo2, you must use explicit interface implementation:

该公开实现了IFoo2接口的类,不是CLS兼容的,为了得到一个CLS兼容的实现IFoo2的类,你必须使用显式的接口实现:

  1. public class MyClass: IFoo2
  2. {
  3.   // explicit interface implementation.
  4.   // DoStuff() is not part of MyClass's public interface
  5.   void IFoo2.DoStuff( UInt32 arg1, string arg2 )
  6.   {
  7.     // content elided.
  8.   }
  9. }

 

MyClass has a CLS-compliant public interface. Clients expecting the IFoo2 interface must access it through the non-CLS-compliant IFoo2 pointer.

MyClass有一个CLS兼容的公共接口。希望访问IFoo2的客户必须通过CLS不兼容的IFoo2指针来访问。

Complicated? No, not really. Creating a CLS-compliant type mandates that your public and protected interfaces contain only CLS-compliant types. It means that your base class must be CLS compliant. All interfaces that you implement publicly must be CLS compliant. If you implement a non-CLS compliant interface, you must hide it from your public interface using explicit interface implementation.

复杂么?一点也不。创建CLS兼容的类型要求你的公共和保护接口仅仅包含CLS兼容的类型。这意味着,你的基类必须是CLS兼容的。你公开实现的所有接口必须是CLS兼容的。如果你实现一个CLS不兼容的接口,你必须使用显式的接口实现来在你的公共接口上隐藏它。

CLS compliance does not force you to adopt a least common denominator approach to your designs and implementations. It means carefully watching the publicly accessible interfaces of your assembly. For any public or protected class, any type mentioned in these constructs must be CLS compliant:

CLS兼容性并不强迫你去使用最小的公共名称来达到你的设计和实现。它意味着,小心使用你的程序集上的公共接口。对于任何公共的或者受保护的类,在构造函数中涉及的任何类型必须是CLS兼容的:

· Base classes基类

· Return values for public and protected methods and properties公共或者保护方法、属性的返回值

· Parameters for public and protected methods and indexers公共或者保护方法和索引器的参数

· Runtime event arguments运行时事件参数

· Public interfaces, declared or implemented公共接口的声明和实现

 

The compiler tries to enforce a compliant assembly. That makes it easy for you to provide some minimum level of CLS support. With a bit of extra care, you can create an assembly that anyone using any language can use. The CLS specification tries to ensure that language interoperability is possible without sacrificing the constructs in your favorite language. You just need to provide alternatives in the interface.

编译器试图强制兼容一个程序集。这会让你提供最小级别上的CLS兼容变得很简单。只需要稍加小心点,你就可以创建使用任何语言的任何人都可以使用的程序集了。在不牺牲你喜欢的语言的结构的情况下,CLS规范试图保证语言互操作性。你仅仅需要在接口上提供选择。

CLS compliance requires you to spend a little time thinking about the public interfaces from the standpoint of other languages. You don't need to restrict all your code to CLS-compliant constructs; just avoid the noncompliant constructs in the interface. The payback of interlanguage operability is worth the extra time.

CLS兼容性要求你花点时间从其它语言的角度来考虑一下公共接口。你不必限制所有的代码都与CLS兼容,只用避免接口中出现不兼容结构就行了。语言的互操作性值得你花点时间。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值