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

来源:互联网 发布:php和mysql 开发工具 编辑:程序博客网 时间:2024/06/09 23:46
 Item 30: Prefer CLS-CompliantAssemblies

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

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

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

CLScompliance is a new twist on that least common denominator approach tointeroperability. The CLS specification is a subset of operations that everylanguage must support. To create a CLS-compliant assembly, you must create anassembly whose public interface is limited to those features in the CLSspecification. Then any language supporting the CLS specification must becapable of using the component. This does not mean you must limit your entireprogramming palette to the CLS-compliant subset of the C# language, however.

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

Tocreate a CLS-compliant assembly, you must follow two rules. First, the type ofall parameters and return values from public and protected members must be CLScompliant. Second, any non-CLS-compliant public or protected member must have aCLS-compliant synonym.

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

Thefirst rule is simple to follow: You can have it enforced by the compiler. Addthe CLSCompliant attribute to your assembly:

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

[assembly: CLSCompliant( true ) ]

Thecompiler enforces CLS compliance for the entire assembly. If you write a publicmethod or property that uses a construct that is not compliant with CLS, it'san error. That's good because it makes CLS compliance an easy goal. Afterturning on CLS compliance, these two definitions won't compile because unsignedintegers 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.     }

Rememberthat creating a CLS-compliant assembly affects only items that can be seenoutside of the current assembly. Foo and Foo2 generate CLS compliance errorswhen declared either public or protected. However, if Foo and Foo2 wereinternal, or private, they could be included in a CLS-compliant assembly;CLS-compliant interfaces are required only for items that are exposed outsidethe assembly.

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

Whatabout this property? Is it CLS compliant?

属性呢?是CLS兼容的么?

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

Itdepends. 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 asCLS compliant, this property is not CLS compliant. That means that the earlierTheProperty is CLS compliant only if MyClass resides in a CLS-compliantassembly.

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

Youcannot build a CLS-compliant assembly if you have types in your public orprotected 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 forusers of your component to create CLS-compliant assemblies. They must hide yourtypes and mirror the functionality in a CLS-compliant wrapper. Yes, this can bedone. But, no, it's not a good way to treat the programmers who want to useyour components. It's better to strive for CLS-compliant assemblies in all yourwork: This is the easiest way for clients to incorporate your work in their CLS-compliantassemblies.

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

Thesecond rule is up to you: You need to make sure that you provide alanguage-agnostic way to perform all public and protected operations. You alsoneed to make sure that you do not sneak a noncompliant object through yourinterface using polymorphism.

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

Operatoroverloading is a feature that some love and others hate. As such, not everylanguage supports or allows operator overloading. The CLS standard does nottake a pro or con stance on the concept of operator overloading. Instead, itdefines a function name for each operator: op_equals is the function namecreated when you write an operator = function. op_addis the name for anoverloaded addition operator. When you write an overloaded operator, theoperator syntax can be used in languages that support overloaded operators.Developers using a language that does not support operator overloading must usethe op_ function name. If you expect these programmers to use your CLS-compliantassembly, you should provide a more convenient syntax. That leads to thissimple recommendation: Anytime you overload an operator, create a semanticallyequivalent 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 polymorphicarguments. It's easy to do with event arguments. You can create a type that isnot compliant with CLS and use it where a base type that is CLS-compliant isexpected.

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

Supposethat you created this class derived from EventArgs:

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

 

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

TheBadEventArgs type is not CLS compliant; you should not use it with eventhandlers 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.  

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

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

Thisdiscussion of CLS compliance ends with how CLS-compliant classes implementcompliant or noncompliant interfaces. It can get complicated, but we'llsimplify it. Understanding CLS compliance with interfaces also will help youfully understand what it means to be CLS compliant and how the environmentviews compliance.

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

Thisinterface 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 canimplement that interface in any CLS-compliant class. However, if you declarethis interface in an assembly that is not marked as CLS compliant, the IFoointerface is not CLS compliant. In other words, an interface is CLS compliantonly if it is defined in a CLS-compliant assembly; conforming to the CLS specis not enough. The reason is compiler performance. The compilers check CLScompliance on types only when the assembly being compiled is marked as CLScompliant. Similarly, the compilers assume that types declared in assembliesthat are not CLS compliant actually are not CLS compliant. However, the membersof this interface have CLS-compliant signatures. Even if IFoo is not marked asCLS compliant, you can implement IFoo in a CLS-compliant class. Clients of thisclass could access DoStuff through the class reference, but not through theIFoo reference.

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

Considerthis small variation:

考虑这个小小的变化:

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

A classthat publicly implements IFoo2 is not CLS compliant. To make a CLS-compliantclass 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. }

 

MyClasshas a CLS-compliant public interface. Clients expecting the IFoo2 interfacemust 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 andprotected interfaces contain only CLS-compliant types. It means that your baseclass must be CLS compliant. All interfaces that you implement publicly must beCLS compliant. If you implement a non-CLS compliant interface, you must hide itfrom your public interface using explicit interface implementation.

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

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

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

·Base classes基类

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

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

·Runtime event arguments运行时事件参数

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

 

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

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

CLScompliance requires you to spend a little time thinking about the publicinterfaces from the standpoint of other languages. You don't need to restrictall your code to CLS-compliant constructs; just avoid the noncompliant constructsin the interface. The payback of interlanguage operability is worth the extratime.

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