Valid C # Principle 30: Select CLS-compatibleProgramSet
Item 30: prefer CLS-compliant assemblies
The. NET runtime environment is language independent: developers can write components in different. NET languages. This is often the case in actual development. The Assembly you created must be compatible with the public language system (CLS), so that other developers can use your components in other languages.
CLS must be compatible with each other at least in terms of public naming. The CLs specification is the minimum subset of operations that all languages must support. Creating a CLS-compatible Assembly means that the public interface of the Assembly you created must be restricted by the CLS specification. This component can be used in any other language that meets the CLS specification. However, this does not mean that your entire program must be compatible with the C # Language subset of Cls.
To create a CLS-compatible assembly, you must follow two rules: first, the parameter and the reverse return value from both public and protected members must be CLS-compatible. Second, other public or protected members not compatible with CLS must have CLS-compatible consent objects.
The first rule is easy to implement: you can force the compilation to complete. Add a clscompliant feature to the Assembly:
[Assembly: clscompliant (true)]
The compiler will force the entire Assembly to be CLS compatible. If you write a public method or attribute that uses a structure incompatible with CLs, the compiler will think this is wrong. This is very good because it makes CLS compatible into a simple task. After enabling CLS compatibility, the following two definitions cannot be compiled, because unsigned integers are not compatible with CLS:
// Not CLS compliant, returns unsigned INT:
Public uint32 Foo ()
{
Return _ Foo;
}
// Not CLS compliant, parameter is an unsigned Int.
Public void foo2 (uint32 parm)
{
}
Remember, when creating a CLS-compatible assembly, it is only valid for content that can be accessed outside the current Assembly. When Foo and foo2 are defined as public or protected, errors may occur due to incompatibility with CSL. However, if Foo and foo2 are internal or private, they will not be included in the set of programs to be compatible with CLS; the CLs-compatible interface is required only when the content is exposed to the outside.
What about attributes? Are they compatible with CLS?
Public myclass theproperty
{
Get {return _ myclassvar ;}
Set {_ myclassvar = value ;}
}
This depends on the situation. If myclass is CLS compatible and indicates that it is CLS compatible, this attribute is also CLS compatible. On the contrary, if myclass is not marked as CLS compatible, the attributes are also incompatible with Cls. This means that the preceding theproperty is only compatible with CLS when myclass is in a CLS-compatible assembly.
If your public or protected interfaces are incompatible with CLs, you cannot compile them into CLS-compatible assemblies. As a component designer, if you do not mark the Assembly as CLS compatible, it is difficult for your users to create CLS compatible assembly. They must hide your type and then encapsulate it in CLS compatibility. Indeed, this can complete the task, but it is not a good method for programmers who use components. It is better for you to work hard to make the program compatible with CLS: for users, this is the easiest way to make their program compatible with Cls.
The second rule depends on your own: You must ensure that all public and protected operations are language independent. At the same time, make sure that the multi-state interface you are using does not hide incompatible objects.
This function is reloaded by operators, which is not liked by some people. Likewise, not all languages Support Operator overloading. The CLs standard does not provide positive support for the concept of overload operators, and does not provide a negative solution. Instead, it defines a function for each operator: op_equals is the name of the function corresponding to the = Operator. Op_addis is the function name after the plus sign is overloaded. After you overload the operator, the operator syntax can be used in languages that support Operator overloading. If some developers use languages that do not support Operator overloading, they must use function names such as op. If you want programmers to use your Cls compatible assembly, you should create more convenient syntaxes. To solve this problem, we recommend that you provide an equivalent function whenever you reload the operator:
// Overloaded addition operator, preferred C # Syntax:
Public static Foo operator + (FOO left, foo right)
{
// Use the same implementation as the add method:
Return Foo. Add (left, right );
}
// Static function, desirable for some ages:
Public static Foo add (FOO left, foo right)
{
Return new Foo (left. BAR + right. bar );
}
Finally, note that when using a multi-state interface, the non-CLS types may be hidden in some interfaces. The most common cause is in the event parameters. This will allow you to create some CLS incompatible types, but use the CLS compatible base class.
Suppose you have created a class derived from eventargs:
Internal class badeventargs: eventargs
{
Internal uint32 errorcode;
}
This badeventargs type is incompatible with Cls. You cannot use this parameter on Event handles written in other languages. But polymorphism makes this happen very easily. You just declare the event parameter as the base class: eventargs:
// Hiding the non-compliant event argument:
Public Delegate void myeventhandler (
Object sender, eventargs ARGs );
Public event myeventhandler onstuffhappens;
// Code to raise event:
Badeventargs Arg = new badeventargs ();
Arg. errorcode = 24;
// Interface is legal, runtime type is not:
Onstuffhappens (this, ARG );
The API declaration with eventargs as the parameter is compatible with Cls. However, the actual replacement parameter type is incompatible with Cls. The result is that some languages are unavailable.
Finally, we end the discussion on CLS compatibility by implementing CLS compatibility classes or incompatible interfaces. Compatibility can be achieved, but we can implement it more simply. Understanding the compatibility between CLs and interfaces can also help you fully understand the meaning of CLS compatibility, and understand how the runtime environment views compatibility.
If this interface is defined in the CLS compatible assembly, it is CLS compatible:
[Assembly: clscompliant (true)]
Public interface ifoo
{
Void dostuff (int32 arg1, string arg2 );
}
You can implement it in any CLS-compatible class. However, if you define this interface in a set of programs that are not labeled as CLS compatible, this ifoo interface is not CLS compatible. That is to say, it is not enough for an interface to meet the CLS specification. It must also be defined as CLS compatible in a CSL-compatible assembly. The cause is that the compiler detects the CLS compatibility type only when the Assembly is marked as CLS compatible. Similarly, the compiler always assumes that the types defined in CLS incompatible sets are actually CLS incompatible. However, the members of this interface have CLS compatibility labels. Even if ifoo is not marked as CLS compatible, You can implement this ifoo interface in the CLS compatible class. Customers of this class can access dostuff through class introduction, rather than reference of ifoo interface.
Consider this simple parameter:
Public interface ifoo2
{
// Non-CLS compliant, unsigned int
Void dostuff (uint32 arg1, string arg2 );
}
A class that publicly implements the ifoo2 interface is incompatible with Cls. To enable a class to implement the ifoo2 interface and be CLS compatible, you must use a clear interface definition:
Public class myclass: ifoo2
{
// Explicit interface implementation.
// Dostuff () is not part of myclass's public interface
Void ifoo2.dostuff (uint32 arg1, string arg2)
{
// Content elided.
}
}
Myclass has a CLS-compatible interface. If you want to access the ifoo2 interface, you must access the ifoo2 interface pointer that is not compatible with Cls.
Is it compatible? No, not yet. Creating a CLS compatibility type requires that all public and protected interfaces only contain the CLS compatibility type. This means that the base class of a class must also be CLS compatible. All Implemented interfaces must also be CLS compatible. If you implement an interface that is not compatible with CLs, you must implement a clear interface definition to hide it on the public interface.
CLS compatibility does not force you to use the smallest public name for your design. It only tells you to use the public interfaces on the Assembly with caution. For any public or protected class, any types involved in the constructor must be CLS compatible, which includes:
* Base class
* Value returned from public or protected methods and attributes
* Public and protected methods and indexer Parameters
* Running the current event parameters
* Public interface declaration and implementation
The compiler tries to force compatibility with an assembly. This makes it easy to provide CLS compatibility at the minimum level. With Caution, you can create an assembly that can be used in other languages. The compiler specification attempts to ensure compatibility with other languages as much as possible without sacrificing the structure of your preferred language. You only need to provide an optional solution in the interface.
CLS compatibility requires you to spend some time thinking about public interfaces in other languages. You don't have to limit allCodeCLS compatibility. You only need to avoid incompatible structures in the interface. The operability of a common language is worth some time.
====================================
Item 30: prefer CLS-compliant assemblies
The. NET environment is language agnostic: developers can in1_ate components written in different. net ages 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 versions can use your components.
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.
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.
The first rule is simple to follow: You can have it enforced by the compiler. Add the clscompliant attribute to your assembly:
[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:
// Not CLS compliant, returns unsigned INT:
Public uint32 Foo ()
{
Return _ Foo;
}
// Not CLS compliant, parameter is an unsigned Int.
Public void foo2 (uint32 parm)
{
}
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 cocould be encoded in a CLS-compliant assembly; CLS-compliant interfaces are required only for items that are exposed outside the assembly.
What about this property? Is it CLS compliant?
Public myclass theproperty
{
Get {return _ myclassvar ;}
Set {_ myclassvar = value ;}
}
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.
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 inmo-ate your work in their CLS-compliant assemblies.
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 versions that support overloaded operators. developers using a language that does not support Operator Overloading must use the op _ function name. if you want CT these programmers to use your Cls-compliant assembly, you shocould provide a more convenient syntax. that leads to this simple recommendation: Anytime you overload an operator, create a semantically equivalent function:
// Overloaded addition operator, preferred C # Syntax:
Public static Foo operator + (FOO left, foo right)
{
// Use the same implementation as the add method:
Return Foo. Add (left, right );
}
// Static function, desirable for some ages:
Public static Foo add (FOO left, foo right)
{
Return new Foo (left. BAR + right. bar );
}
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.
Suppose that you created this class derived from eventargs:
Internal class badeventargs: eventargs
{
Internal uint32 errorcode;
}
The badeventargs type is not CLS compliant; You shocould not use it with Event Handlers written in other ages. but polymorphism makes this easy to do. you can declare the event type to use the base class, eventargs:
// Hiding the non-compliant event argument:
Public Delegate void myeventhandler (
Object sender, eventargs ARGs );
Public event myeventhandler onstuffhappens;
// Code to raise event:
Badeventargs Arg = new badeventargs ();
Arg. errorcode = 24;
// Interface is legal, runtime type is not:
Onstuffhappens (this, ARG );
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 versions ages cannot use.
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.
This interface is CLS compliant if it is declared in a CLS-compliant assembly:
[Assembly: clscompliant (true)]
Public interface ifoo
{
Void dostuff (int32 arg1, string arg2 );
}
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 cocould access dostuff through the class reference, but not through the ifoo reference.
Consider this small variation:
Public interface ifoo2
{
// Non-CLS compliant, unsigned int
Void dostuff (uint32 arg1, string arg2 );
}
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:
Public class myclass: ifoo2
{
// Explicit interface implementation.
// Dostuff () is not part of myclass's public interface
Void ifoo2.dostuff (uint32 arg1, string arg2)
{
// Content elided.
}
}
Myclass has a CLS-compliant public interface. Clients expecting the ifoo2 interface must access it through the non-CLS-compliant ifoo2 pointer.
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 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:
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 sacrifle icing the constructs in your favorite language. you just need to provide alternatives in the interface.
CLS compliance requires you to spend a little time thinking about the public interfaces from the standpoint of other ages. 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.