By now, it should be obvi ous to you that the CLR is all about types. Types expose functionality to your applications and components. Types are the mechanism by which code written in one programming language can talk to code written in a different programming language. Because types are at the root of the CLR, Microsoft created a formal specification—the Common Type System (CTS)—that describes how types are defined and how they behave.
The CTS specification states that a type can contain zero or more members. In Part III, I’ll cover all these members in great detail. For now, I just want to give you a brief introduction to them:
Field A data variable that is part of the object’s state. Fields are identified by their name and type.
Method A function that performs an operation on the object, often changing the object’s state. Methods have a name, a signature, and modifiers. The signature specifies the calling convention, the number of parameters (and their sequence), the types of the parameters, and the type of value returned by the method.
Property To the caller, this member looks like a field. But to the type implementer, it looks like a method (or two). Properties allow an implementer to validate input parameters and object state before accessing the value and/or calculate a value only when necessary. They also allow a user of the type to have simplified syntax. Finally, properties allow you to create read-only or write-only “fields.”
Event An event allows a notification mechanism between an object and other interested objects. For example, a button could offer an event that notifies other objects when the button is clicked.
The CTS also specifies the rules for type visibility and for access to the members of a type.
For example, marking a type as public (called public) exports the type, making it visible and accessible to any assembly. On the other hand, marking a type as assembly (called internal in C#) makes the type visible and accessible to code within the same assembly only. Thus, the CTS establishes the rules by which assemblies form a boundary of visibility for a type, and the CLR enforces the visibility rules.
Regardless of whether a type is visible to a caller, the type gets to control whether the caller has access to its members. The following list shows the valid options for controlling access to a method or a field:
Private The method is callable only by other methods in the same class type.
Family The method is callable by derived types, regardless of whether they are within the same assembly. Note that many languages (such as C++ and C#) refer to family as
Family and assembly The method is callable by derived types, but only if the derived type is defined in the same assembly. Many languages (such as C# and Visual Basic) don’t offer this access control. Of course, IL Assembly language makes it available.
Assembly The method is callable by any code in the same assembly. Many languages refer to assembly as internal.
Family or assembly The method is callable by derived types in any assembly. The method is also callable by any types in the same assembly. C# refers to family or assembly as protected internal.
Public The method is callable by any code in any assembly. n addition, the CTS defines the rules governing type inheritance, virtual functions, object lifetime, and so on. These rules have been designed to accommodate the semantics expressible in modern-day programming languages. In fact, you won’t even need to learn the CTS rules per se since the language you choose will expose its own language syntax and type rules in the same way you’re familiar with today and will map the language-specific syntax into the “language” of the CLR when it emits the managed module.
When I first started working with the CLR, I soon realized that it is best to think of the language and the behavior of your code as two separate and distinct things. Using C++, you can define your own types with their own members. Of course, you could have used C# or Visual Basic to define the same type with the same members. Sure, the syntax you use for defining this type is different depending on the language you choose, but the behavior of the type will be absolutely identical regardless of the language because the CLR’s CTS defines the behavior of the type.
To help clarify this idea, let me give you an example. The CTS supports single inheritance only. So, while the C++ language supports types that inherit from multiple base types, the CTS can’t accept and operate on any such type. To help the developer, the Visual C++ compiler reports an error if it detects that you’re attempting to create managed code that includes a type inherited from multiple base types.
Here’s another CTS rule. All types must (ultimately) inherit from a predefined type:
System.Object. As you can see, Object is the name of a type defined in the System namespace. This Object is the root of all other types and therefore guarantees every typeinstance has a minimum set of behaviors. Specifically, the System.Object type allows youto do the following:
Compare two instances for equality
Obtain a hash code for the instance
Query the true type of an instance
Perform a shallow (bitwise) copy of the instance
Obtain a string representation of the instance’s object’s current state