Architecture of the .NET Framework
The term .NET Framework refers to the group of technologies that form the development foundation for the Microsoft .NET platform. The key technologies in this group are the runtime and the class libraries.
The runtime is responsible for managing your code and providing services to it while it executes, playing a role similar to that of the Visual Basic 6.0 runtime. The .NET programming languages—including Visual Basic .NET, Microsoft Visual C#™, C++ managed extensions, and many other programming languages from various vendors—utilize .NET services and features through a common set of unified classes.
The .NET unified classes provide the foundation on which you build your applications, regardless of the language you use. Whether you are simply concatenating a string, or building a Windows Service or a multiple-tier Web-based application, you will be using these unified classes.
The unified classes provide a consistent method of accessing the platform's functionality. Once you learn to use the class library, you'll find that all tasks follow the same uniform architecture. You no longer need to learn and master different API architectures to write your applications.
It also becomes easier to deploy your Visual Basic .NET applications, thanks to the .NET Framework. Unlike Visual Basic 6.0 applications, you won't need to deploy myriad dependencies, such as a separate data access library, XML parser, and network API, because all of this functionality is part of the .NET Framework.
By building your applications on a unified, integrated framework, you maximize your return on the time you spend learning this framework, and you end up with more robust applications that are easy to deploy and maintain.
Why do we need .NET Framework
The purpose of computers is to manage information. The nature of the information varies a lot from application to application, but at the core it is still all about information. In recent years, the Internet has largely convinced the general public that computers, indeed, have the potential to simplify their lives in terms of information management. As a result the consumer’s expectation of the Internet and technology in general has taken a huge leap!
People need to create information easily. They need to move, share, and organize information flexibly. They need to know what is going on, find the latest price, and get to their movie on time. People have stopped being impressed merely by the fact that computers work at all. They now want computers to tell them where they can get a New York steak in a semi-romantic setting within five miles at 7:30 tonight; "OK, now make a reservation for two." They want their shipping information to be remembered from one web site to the next. And each web site should be personalized to individual tastes as well. All of these "needs" are about the organization and communication of information. By the way, most of this information already exists without computers or the Internet (such as which restaurants have available tables, or the fact that a customer wants to make reservations), but customers increasingly expect technology to move and react to this information naturally.
For all of this to happen, somebody somewhere has to write software. But this isn’t the software that you pick-up off the shelf and install on the computer. Half of the software required to implement all of this doesn’t even run on the consumer’s computer. This is called software as a service. Remember this term.
People will pay to be served. And if your company’s service deals with information or communication, then you need software as a service to serve your customer effectively. People also want these services to come from thousands of vendors, not just one. This will improve both the quality and the price of whatever it is they are buying. Additionally many of these services will have to work together seamlessly no matter who implemented them. Suddenly, the demands on the average software developer are getting pretty steep!
Before talking solutions, I want to describe a specific scenario. Imagine that you are a realtor and you want to create a website that your client can use to keep track of their home buying or selling process. I have seen real estate sights that offer some nice features such as up-to-date listings, mortgage calculators, and the like. Now imagine that you want to integrate some more advanced features such as real-time loan-approval status, or escrow status. Suddenly the computer that runs your website has to communicate information with computers from the escrow company and the bank. This isn’t an impossible problem to solve, but it gets tougher when you consider that the customer wants to choose from a selection of escrow companies and you don’t want to lose their business just because you don’t support a particular escrow company’s data-protocol.
Tough problems like these have been solved in the past by defining standards. Standards are great, but they carry with them the burden of meeting everyone’s needs. This often makes their creation slow, and their implementations tedious. If you were this realtor, what you really want are a few general-purpose standards and a development platform robust and simple enough to meet your agile business needs. Imagine that it only took your web-developer a half-day to understand and incorporate a new escrow company’s data interface and another half-day to test it. All of the communication details just worked. And your developer did the whole thing without once picking up the phone and calling a developer from the escrow company. Now you are in business!
This is Microsoft’s .NET Initiative. And in reality .NET is just the cherry on the tree that is an entire industry swaying in this direction. Standard protocols like SOAP will make data exchange so simple that your software will be able to keep up with your business. Simple data standards like XML will expose your businesses information to anyone who needs to consume it and vice-versa. Finally, the platform that brings these features to developers in a simple, consistent, reliable, and scalable fashion will be a major contender for the foreseeable future. This platform is The Microsoft .NET Frameworks.
1. Common Language Runtime
The .NET Framework provides a run-time environment called the Common Language Runtime, which manages the execution of code and provides services that make the development process easier. Compilers and tools expose the runtime's functionality and enable you to write code that benefits from this managed execution environment. Code that you develop with a language compiler that targets the runtime is called managed code; it benefits from features such as cross-language integration, cross-language exception handling, enhanced security, versioning and deployment support, a simplified model for component interaction, and debugging and profiling services.
To enable the runtime to provide services to managed code, language compilers are required to emit metadata, which means that they provide information that describes the types, members, and references in your code. Metadata is stored along with the code: every loadable common language runtime image contains metadata. The runtime uses metadata to locate and load classes, lay out instances in memory, resolve method invocations, generate native code, enforce security, and set up run time context boundaries.
The runtime automatically handles object layout and manages references to objects, releasing them when they are no longer being used. Objects whose lifetimes are managed in this way by the runtime are called managed data. Automatic memory management eliminates memory leaks as well as some other common programming errors. If your code is managed, you can use managed data; however, you can instead use unmanaged data if you wish, or you can use both managed and unmanaged data in your .NET application. Because language compilers supply their own types, such as primitive types, you might not always know (or need to know) whether your data is being managed.
The Common Language Runtime makes it easy to design components and applications whose objects interact across languages. Objects written in different languages can communicate with each other, and their behaviors can be tightly integrated. For example, you can define a class, then, using a different language, derive a class from your original class or call a method on it. You can also pass an instance of a class to a method on a class written in a different language. This cross-language integration is possible because language compilers and tools that target the runtime use a common type system defined by the runtime, and they follow the runtime's rules for defining new types, as well as creating, using, persisting, and binding to types.
As part of their metadata, all managed components carry information about the components and resources they were built against. The runtime uses this information to ensure that your component or application has the specified versions of everything it needs; as a result, your code is less likely to break due to some dependency not being met. Registration information and state data are no longer stored in the registry where it can be difficult to establish and maintain; instead, information about the types you define (and their dependencies) is stored with the code as metadata, making the tasks of component replication and removal much less complicated.
Language compilers and tools expose the runtime’s functionality in ways that are intended to be useful and intuitive to their developers. This means that some features of the runtime might be more noticeable in one environment than in another. Therefore, how you experience the runtime depends on which language compilers or tools you use. For example, if you are a Visual Basic developer, you might notice that with the Common Language Runtime, the Visual Basic language has more object-oriented features than before. The following benefits of the runtime might be particularly interesting to you:
1 Performance improvements.
2 The ability to easily use components developed in other languages.
3 Extensible types provided by a class library.
4 A broad set of language features.
If you use Visual C++, you can write managed code using managed extensions to C++, which provide the benefits of a managed execution environment while giving you access to powerful capabilities and expressive data types that you are familiar with. You might find the following runtime features especially compelling:
1 Cross-language integration, especially cross-language inheritance.
2 Automatic memory management, which manages object lifetime so that reference counting is unnecessary.
3 Self-describing objects, which make using IDL (Interface Definition Language) unnecessary.
4 The ability to compile once and run on any CPU and operating system that supports the runtime.
2. Understanding Managed Execution
The first step in the managed execution process is designing the source code. If you want your application to have the benefits provided by the Common Language Runtime, you must use one (or more) language compilers that target the runtime, such as Visual Basic, C#, Visual C++, or one of many third party compilers such as a Perl or COBOL compiler.
Because the runtime is a multi-language execution environment, it supports a wide variety of data types and language features. The language compiler you are using determines what subset of the runtime's functionality is available, and you design your code using the features available in your compiler. The syntax you use in your code is determined by the compiler, not by the runtime. If your component is required to be completely usable by components written in other languages, you must use only language features that are included in the Common Language Specification (CLS) in your component's exported types.
Once your code is written, you compile it, and the compiler translates it to Microsoft intermediate language (MSIL) and generates the required metadata. When you are ready to execute your code, the MSIL gets compiled into native code by a Just In Time (JIT) compiler. If security policy requires the code to be type-safe, the IL is checked for type-safety as part of the JIT compilation process; if the type-safety check fails, an exception is thrown when the code is executed.
During execution, the runtime provides services that include automatic memory management, debugging support, enhanced security, and interoperability with unmanaged code, such as COM components. The following topics provide more detailed information about managed execution:
1 Microsoft Intermediate Language (MSIL)
2 JIT Compilation
5 Application Domains
6 Runtime Hosts
7 Microsoft Intermediate Language (MSIL) click here for more…
If you compile your source code to managed code, the compiler translates your source code into Microsoft intermediate language (MSIL), which is a CPU-independent set of instructions that can be efficiently converted to native code. MSIL includes a wide spectrum of instructions, such as instructions for loading, storing, initializing, and calling methods on objects, as well as instructions for arithmetic and logical operations, control flow, direct memory access, and exception handling. Before code can be executed, MSIL must be converted to CPU-specific code by a just in time (JIT) compiler. Because the runtime supplies one or more JIT compilers for each computer architecture it supports, the same set of MSIL can be JIT-compiled and executed on any supported architecture.
When a compiler produces MSIL, it also produces metadata, which describes the types in your code, including the definition of each type, the signatures of each type's members, the members that your code references, and other data that the runtime uses at execution time. The MSIL and metadata are contained in a portable executable (PE) file that is based on and extends the published Microsoft Portable Executable (PE) and Common Object File Format (COFF) used historically for executable content. This file format, which accommodates MSIL or native code as well as metadata, enables the operating system to recognize Common Language Runtime images. The presence of metadata in the file along with the MSIL enables your code to describe itself, which means that there is no need for type libraries or IDL. The runtime locates and extracts the metadata from the file as necessary during execution.
2 JIT Compilation
Before MSIL can be executed, it must be converted by a .NET Framework Just In Time (JIT) compiler to native code, which is CPU-specific code that runs on the same computer architecture that the JIT compiler is running on. Because the runtime supplies a JIT compiler for each CPU architecture that the runtime operates on, developers can write a set of MSIL that can be JIT-compiled and executed on computers with different architectures. (If your managed code calls platform-specific native APIs or a class libary that is platform-specific, your code is limited to running on a specific operating system.)
The idea behind JIT compilation recognizes the fact that some code may never get called during execution; therefore, rather than using time and memory to convert all of the MSIL in a PE (portable executable) file to native code, it makes sense to convert the MSIL as it is needed during execution and store the resulting native code so that it is accessible for subsequent calls. The loader creates and attaches a stub to each of the type's methods when the type is loaded; on the initial call to the method, the stub passes control to the JIT compiler, which converts the MSIL for that method into native code and modifies the stub to direct execution to the location of the native code. Subsequent calls of the JIT-compiled method proceed directly to the native code that was previously generated, reducing the time it takes to JIT compile and execute the code.
As part of compiling MSIL to native code, the code must pass a verification process (unless an administrator has established a security policy that allows the code to bypass verification). Verification examines MSIL and metadata to see whether the code can be determined to be type-safe, which means that it is known to access only the memory locations it is authorized to access. Type safety is necessary to ensure that objects are safely isolated from each other and therefore safe from inadvertent or malicious corruption. It also provides assurance that security restrictions on code can be reliably enforced.
For code that is verifiably type-safe, the runtime can rely on the following statements being true:
1 A reference to a type is strictly compatible with the type being referenced.
2 Only appropriately defined operations are invoked on an object.
3 Identities are what they claim to be.
During the verification process, MSIL code is examined in an attempt to confirm that the
ode can access memory locations and call methods only through properly defined types. For example, code cannot allow an object's fields to be accessed in a manner that allows memory locations to be overrun. Additionally, verification inspects code to see whether the MSIL has been correctly generated, since incorrect MSIL could lead to a violation of the type safety rules. The verification process passes a well-defined set of type-safe code, and it passes only code that is type-safe. However, some type-safe code might not pass verification due to limitations of the verification process, and some languages by design do not produce verifiably type-safe code.
The Common Language Runtime provides the infrastructure that enables execution to take place as well as a variety of services that can be used during execution. Before a method can be executed, it must be compiled to processor-specific code. Each method for which MSIL has been generated is JIT-compiled when it is called for the first time, then executed. The next time the method is executed, the existing JIT-compiled native code is executed. The process of JIT compiling and then executing the code is repeated until execution is complete.
During execution, managed code receives services such as automatic memory management, security, interoperability with unmanaged code, cross-language debugging support, and enhanced deployment and versioning support.
Interoperating with Unmanaged Code
The Microsoft .NET Framework provides the infrastructure to help you create .NET applications that interact with COM components, COM+ services, external type libraries, and many operating system services. By interoperating with existing code, you can preserve your current code base and upgrade at your own pace.
The Common Language Runtime manages all code that runs inside the .NET Framework. Code that executes under the control of the runtime is called managed code. Conversely, code that runs outside the runtime is called unmanaged code. For example, COM components, ActiveX interfaces, and Win32 API functions are all unmanaged.
Data types, method signatures, and error handling mechanisms vary between managed and unmanaged object models. To simplify interoperation between the .NET Framework components and unmanaged code and to ease the migration path, the runtime conceals from both clients and servers the differences in these object models.
This guide to interoperations introduces two levels of topics. The first collection of topics, called Getting Started, identifies the tasks for using the .NET Framework interoperation services. In some cases, you might need additional information about how the interoperation services work and what you might do to modify their behavior. The second collection of topics provides such detailed information.
Deploying Common Language Runtime Applications
Because the Common Language Runtime assemblies are self-describing and define the relationships of their components, application installation and deployment is greatly simplified. Runtime applications do not require any registry entries; deploying a runtime application can be as simple as copying the assemblies that constitute an application to a directory structure on disk. The runtime can then launch the application and resolve all references directly from the file system. Because each assembly describes where to locate the classes it contains, there is no need to make registry entries of any kind.
To deploy runtime files, you can create either .msi files for use with the Windows Installer or.cab files for distribution by downloading or copying. If you intend to deploy files from a website, assemblies are downloaded automatically into the download cache when they are referenced.
The following shows the "lifecycle" of an assembly's files as they relate to standard packaging and distribution:
1. Source files are compiled and linked into .exe's, .dll's and .resources files (other file types, like .gif can be included in an assembly as well).
2. The assemblies created by the build step can be deployed as is simply by xcopy'ing them to a directory. Also, .dll's can be referenced by codebases and downloaded on demand.
3. Alternatively, a developer may choose to package the assembly files into some other distribution unit like compressed .cab files or .msi files that can be installed by the Windows Installer. CAB files can also be distributed through code download and installed onto the client machine by the client.
The Common Language Runtime provides a secure, lightweight and versatile unit of processing called an application domain. Application domains benefit from .NET Framework security features and are the unit of enforcing security policy. Application domains also provide fault tolerance at a much smaller resource cost than with a traditional process. In fact, multiple application domains can run in a single Win32 process.
Operating systems and runtime environments typically provide some form of isolation between applications running on the system. This isolation is necessary to ensure that code running in one application cannot adversely affect other unrelated applications.
Typically, isolation means
1 Faults in one application cannot affect other applications by bringing down the entire process.
2 Applications can be independently stopped and debugged.
3 Code running in one application cannot directly access code or resources from another application; doing so could introduce a security hole.
4 The behavior of running code is scoped by the application it runs in.
In modern operating systems, this isolation has historically been achieved using process boundaries. A process runs exactly one application and that process scopes the resources that are available for that process to use. For example, memory addresses in Win32 are process relative---a pointer in one process is meaningless in the context of another process.
The Common Language Runtime relies on the fact that code is type safe and verifiable to provide fault isolation between domains. By relying on the type safe nature of the code, application domains provide fault isolation at a much lower cost than the process isolation used in Win32. Because isolation is based on static type verification, there is no need for hardware ring transitions or process switches. In many respects, application domains are the Common Language Runtime equivalent of a Win32 process.
The runtime creates an application domain for each runtime application; each application domain can have an associated configuration file. Application domains isolate separate applications which run within a process. The combination of an application domain and configuration information create isolation for the application.
The way in which application domains are created affects the permissions which assemblies have when running in the domain. For basic web-application scenarios, where the web page does not provide a LINK tag to a configuration file, the runtime creates an application domain on a per-site basis. Domain-neutral assemblies are only shared between application domains and assemblies with identical permission sets. A domain-neutral assembly called by two application domains with dissimilar permission sets is loaded into the domain neutral area twice. With the exception of mscorlib of which there is only one copy of the code, all statics and local data are cloned.
An application can have multiple application domains running on its behalf. An application domain is the unit of code loading and unloading. Note that you can have multiple application domains in one process. Each type is loaded into the same application domain as its caller, or a caller can request that a type be loaded into a new application domain. There are no direct calls between objects in different application domains; instead, a proxy is used. Application domains can be debugged independently.
The runtime is typically started and managed by environments like ASP.NET, IE or the Windows Shell. These hosting environments run managed code on behalf of the user and take advantage of the application isolation features provided by application domains. In fact it is the host that determines where the application domain boundaries lie and in what application domain user code is run in. The Common Language Runtime provides a set of classes and interfaces used by hosts to create and manage Application Domains.
There are five Common Language Runtime hosts:
1 ASP.NET - ASP.NET creates application domains to run user code. Application domains are created per application as defined by the web server.
2 Internet Explorer - IE creates an application domain per site.
3 Windows Shell EXE - Each application that is launched from the command line runs in a separate application domain.
4 VBA - VBA runs the script code contained in an Office document in an application domain.
5 Windows Forms Designer - The Windows Forms Designer places each form the user is building in a separate application domain. When the user edits the form and rebuilds, Windows Forms shuts down the old application domain, recompiles the code and runs it in a new application domain.
The Common Type System
The common type system describes the types supported by the runtime and specifies how those types can interact with each other and how they are persisted in metadata. A type defines allowable values and the operations supported by those values. The types in the runtime's type system include classes, interfaces, and value types. Types can have methods that describe the operations on the type as well as other members such as fields, properties, and events.
The type system is an important part of the runtime's support for cross-language integration because it provides the rules that language compilers follow with respect to defining, referencing, using, and storing types. The fact that types are created and used consistently by language compilers supplies the basis for ensuring that objects written in different languages can interact with each other.
The Microsoft .NET Framework is built on the type system that the runtime defines. It supplies built in primitive types as well as other types that you can use or derive from when building your component or application.
Metadata and Self Describing Components
In the past, a compiled component (.exe or .dll) was only able to communicate with another compiled component through a binary interface. Furthermore, since different languages sometimes had conflicting protocols for defining and storing data, inter-language communication was made difficult. One way that the .NET Framework solves these problems is by allowing compilers to emit additional declarative information into all .NET Framework files and assemblies. This information, called metadata, serves as a roadmap for compiled files to seamlessly interact. Metadata, however, does not work alone to solve these and other problems. It is aided by additional components of the Common Language Runtime like the common type system.
Metadata is binary information describing your code that is either stored in a .NET Framework portable executable (PE) file or in memory. When your code is compiled into a PE file, metadata is inserted into one portion of the file while your code is converted to Microsoft intermediate language (MSIL) and inserted into another. Every type and member defined and referenced in a file or assembly is described within metadata. When code is executed, the Common Language Runtime loads metadata information into in-memory data structures that it references when it requires information about your code's classes, members, inheritance, etc. Runtime reflection services are used to retrieve information from in-memory data structures.
Many key .NET Framework benefits stem from the runtime's use of metadata. Metadata provides a common frame of reference that enables communication between the runtime, compilers, debuggers, and code that has been compiled into MSIL. It enables the runtime to locate and load your code, generate native code at runtime, and provide memory management services. The runtime is able to track which portions of memory your code is allowed to access and prevent it from accessing memory that it shouldn't. Metadata also helps the runtime and garbage collection keep track of memory that will be released back to the operating system when it is no longer needed. The information stored in metadata also enables the Common Language Runtime to enforce security by tracking the access-privileges that your code requests and is granted.
The following list is a more precise explanation of the information stored in metadata:
Description of PE or assembly
1 Identity: name, version, culture, public key
2 What types are exported
3 Other assemblies on which it depends
4 Security permissions needed to run
Description of types
1 Name, visibility, base class, interfaces implemented
2 Members (methods, fields, properties, events, nested types)
1 Additional descriptive elements that modify types and members
The Benefits of Metadata
Metadata provides so much information about compiled code that you can actually inherit a class from a PE file written in a different language. You can create an instance of any accessible class in PE files written in any managed language without having to worry about explicit marshalling or using custom interoperability code. The Common Language Runtime allows you to access compiled code in a manner that was previously only possible with non-compiled files.
.NET Framework files and assemblies are self-describing. Everything one component needs in order to interact with another component is provided within that component's metadata. This architecture eliminates the need for IDL files, type libraries, or any external method of component reference. .NET Framework files and assemblies do not even require registration with the operating system. In this manner, the descriptions used by the runtime will always reflect the actual code inside your compiled file and application reliability will be increased.
Since the compiler is responsible for creating metadata, you do not directly control much of what is emitted. However, the Common language Runtime allows you to declare specific kinds of metadata, called attributes, in your compiled file. Attributes can be found throughout the .NET Framework and are used to control in more detail how your program behaves at runtime. Additionally, you can emit your own custom metadata into .NET Framework files through the use of user-defined custom attributes
Before the Common Language Runtime was available, objects compiled by different language compilers could communicate only if they followed an established binary standard. .NET Framework objects automatically have the ability to communicate and interact with each other, even if they are written in different languages. Your objects can call methods on other objects, inherit implementation from other objects, and pass instances of a class to another class's methods, regardless of the language they are implemented in. In addition, the runtime’s language interoperability enables any debugger to understand your multi-language application and step through it. Exception handling reveals another aspect of language interoperability: the runtime enables exceptions to be handled the same way across languages. Your code can throw an exception in one language and that exception can be caught and understood by an object written in another language.
In some scenarios, language interoperability is particularly desirable. For example, if you are writing components for a distributed Web application, it would be helpful to know that no matter what language you choose to write your components in, they can interact closely with each other and with components supplied by other developers.
Or, suppose you have developed several components for your server application in one language, but you realize that another language is better suited for implementing the functionality that the remaining component provides. Or, maybe an existing corporate code base is in one language and you are asked to enhance it, but you are much more comfortable using a different programming language or you would like to leverage your development staff's experience with another language. It would be convenient to be able to implement the remaining components in the language that is best suited for the job and/or most efficient for you to use.
Language interoperability is also significant with respect to class library development. Because the runtime enables and supports language interoperability, you can develop class libraries that can be called from any language.
Common Language Specification
For objects to be able to understand each other no matter what language compiler they are compiled with, they must expose to callers only those data types and features that are common to all the languages they need to interoperate with. For this reason, the runtime has identified a set of language features called the Common Language Specification (CLS), which includes the basic language features needed by many applications. If your component uses only CLS features in the API that it exposes to other code, including subclasses, the component is guaranteed to be accessible from any object compiled by a language compiler that supports the CLS. Components that adhere to the CLS rules and use only the features included in the CLS are said to be CLS-compliant components.
The following list summarizes the types and features that are in the CLS. This list is intended to be informative, but not comprehensive. The primitive types listed are the types in the .NET Framework, with a description of each type in parentheses.
CLS Feature Description
System.Boolean (true or false)
System.Char (2-byte unsigned integer)
System.Byte (1-byte unsigned integer)
System.Int16 (2-byte signed integer)
System.Int32 (4-byte signed integer)
System.Int64 (8-byte signed integer)
System.Single (4-byte floating point number)
System.Double (8-byte floating point number)
System.String (A string of zero or more characters; null is allowed.)
System.Object (The root of all class inheritance hierarchies)
Can have known type, known ( >= 1) dimension (rank), with zero lower bound.
Element type must be a CLS type.
Can be abstract or sealed (but not both).
Can implement zero or more interfaces; different interfaces can have methods with the same name and signature.
Can be derived from exactly one type and override or hide members provided by that type.
Can have zero or more members, which are fields, methods, events, or types.
Can have zero or more constructors.
Can have public or assembly visibility, but only public members are considered part of the "public" interface of the type.
Value types must inherit from System.ValueType, unless they are enumerations, in which case they inherit from System.Enum.
Members can override or hide other members in another type.
Argument types and return types must be CLS-compliant types.
Constructors, methods, and properties can be overloaded.
Members can be abstract, but not if the type is sealed.
Can have public. private, family, assembly, FamilyAndAssembly, or FamilyOrAssembly visibility, but only Public, Family and FamilyOrAssembly are considered part of the "public" interface of the type.
Can be one of virtual, instance, or static.
Virtual and instance methods can be abstract or concrete (static methods must be concrete).
Virtual methods can be final (or not).
Can be static or non-static.
Static fields can be initonly or literal.
Can be exposed as Get and Set methods instead of using property syntax.
Return type of the Getter and the first argument of the setter must be identical; this is the "property type."
Cannot differ by the "property type" alone.
If an X property is defined, you cannot define Get_X and Set_X methods in the same class.
Can be indexed.
Must follow this naming pattern: get_, set_.
Underlying type must be Byte, Int16, Int32, or Int64.
Each member is a static literal field of the enum's type.
Cannot implement any interfaces.
Multiple fields can be assigned the same value.
Must inherit from System.Enum.
Can be thrown and caught.
Must inherit from System.Exception.
Can require implementation of other interfaces.
Can define properties, events, and virtual methods.
Add and remove methods must be either both provided or both absent; each of these methods take one parameter, which is a class derived from System.Delegate.
Must following this naming pattern: add_, remove_, and raise_.
Can use only the following types: Type, String, Char, Boolean, Byte, Int16, Int32, Int64, Single, Double, Enum (of a CLS type), Object.
Can be created and invoked.
The first character must come from a restricted set.
Case cannot be used to distinguish between identifiers within a single name scope (i.e, types within assemblies, members within types).
Writing CLS-Compliant Code
If you want your component or application to be CLS-compliant, you must use only CLS features in the following places:
1 in the definitions of your public classes.
2 in the definitions of the public members of public classes, and in the definitions of members accessible to subclasses.
3 in the parameters of public methods of public classes, and in the parameters of methods accessible to subclasses.
However, it doesn't matter what features you use in the definitions of your private classes, in the definitions of private methods on public classes, and in local variables. Also, in the code that implements your class, you can use any language features you want and still have a CLS-compliant component.
Many language compilers targeting the runtime have agreed to support the data types and features in the CLS. These language compilers simplify CLS-compliance by making the CLS data types and features available to you so that you can create components using them. The levels of CLS-compliance among compilers and other tools are described as follows:
CLS-compliant Consumer Tools:
Language compilers that enable developers to access all the features supplied by CLS-compliant libraries. Developers using these languages might not be able to extend these libraries by creating new types, but they can use any type defined by a CLS-compliant library.
CLS-compliant Extender Tools:
Language compilers that allow developers to both use and extend types defined in CLS-compliant libraries. Developers can use existing types as well as define new types.
When you design CLS-compliant components, it is helpful to use a language compiler that has the ability to produce CLS-compliant components. In some cases, these language compilers will assist you by providing a "switch" that you can throw to tell the compiler that you want your code to be CLS-compliant; the compiler will let you know when your code uses functionality that is not supported by the CLS. You could write CLS-compliant components without using a CLS-compliant language compiler, but it will probably be more difficult for two reasons: 1) you must keep track of CLS features yourself and make sure that you use only those features and follow CLS rules; 2) the compiler might not offer all the CLS features you would like to use.
If you design a CLS–compliant framework (class library), your library will be usable from many programming languages, including code written using CLS-compliant consumer tools and code written using CLS-compliant extender tools