Types and Common Type System


What is Type in .NET Framework

The .NET Framework is built around types. A type in .NET is a class, structure, interface, enumeration, or delegate. A type is the fundamental unit of programming in .NET. In C#, a type can be declared using the class, structure, and interface keywords. Every piece of code that you write in .NET, even the main program for your application, must be a member of some type.

In .NET there are two main classifications of types and every type is derived from a Root Reference Type named System.Object (directly or indirectly through another base type).

  • Value Type
      • User Defined Value Types (Structures)
      • Enumeration
  • Reference Type
      • User Defined Types (Classes)
      • Array
      • Delegate

    The runtime requires every type to ultimately derive from System.Object type. This means that the following code is identical

    // Implicitly derived from Object
    class Employee {
    ….
    }
    //Explicitly derived from Object
    class Employee : System.Object {
    ….
    }

    Every instance in .NET is derived from System.Object, so it is guaranteed that every object of every type has a minimum set of methods. Explicitly, System.Object class offers the public listed methods given below

    Public class Object {
    public virtual bool Equals(object);
    public virtual int GetHashCode();

    public virtual string ToString();

    public Type GetType();

    // static
    public static bool Equals(object, object);

    //protected
    ~Object();  // Finalize
    protected object MemberwiseClone();
    }

    Public Methods Description
    Equals Returns true if two objects have the same value
    GetHashCode Returns a hash code for the objects type. A type should override this method if its objects are to be used as a key in a hash table collection. The method should provide a good distribution for its objects.
    ToString returns the full name of the type. However it is common to override this method so that it returns a String object containing a representation of the object state. For e.g. the core types such as Boolean and Int32, override this method to return a string representation of their values.
    Gettype Returns an instance of a type derived object that identifies the type of the object used to call GetType. The returned Type object can be used with the reflection classes to obtain metadata information about the object’s type.
    Protected Methods Description
    MemberwiseClone This nonvirtual method creates a new instance of the type and sets the new object’s instance to be identical to this object’s instance fields. A reference to the instance is returned.
    Finalize This virtual method is called when the garbage collector determines that object is garbage before memory for the object is reclaimed. Types that require cleanup when collected should override this method.

    The advantage of deriving from System.Object means that code is verify If all of your code is a member of a type, as long as you can guarantee type safety—in other words, as long as you can guarantee that it is impossible to coerce an object of one type into behaving like another type that it is not assignment compatible with you can go a long way toward guaranteeing that the code is safe. In addition to making it easy to write code that is more secure, less error prone, and easier to debug,

    Microsoft also wants to enable an unprecedented level of  cross-language interoperability in the .NET Framework. One of the key enabling technologies in the .NET Framework that makes this possible is the Common Type System (CTS). The CTS provides a common set of types for all CLR-compliant languages. With the CTS, Microsoft has created a type system that all CLR-compliant programming languages share. Table 3–1 shows a list of the types supported by the CTS. The definitions of all of these types can be found in the System namespace in the Framework libraries,

    Figure below  Widely Used Types in the .NET Framework  (REFERENCE mdsn)

    Common Types System

    Common Types System

    Type

    Inheritance

    Properties and Fields

    Methods

    Total

    System.Int32

    0

    8438

    6756

    15194

    System.String

    0

    2406

    6484

    8890

    System.Object

    2779

    456

    2947

    6182

    System.IntPtr

    0

    397

    1661

    2058

    System.Boolean

    0

    943

    1096

    2039

    System.EventHandler

    0

    4

    1766

    1770

    System.IComparable

    1158

    0

    0

    1158

    System.IConvertible

    1139

    0

    0

    1139

    System.IFormattable

    1135

    0

    0

    1135

    System.Enum

    1120

    0

    0

    1120

    System.Type

    5

    48

    659

    712

    System.Runtime.Serialization.ISerializable

    617

    0

    0

    617

    System.IDisposable

    602

    0

    0

    602

    System.Single

    0

    86

    505

    591

    System.ICloneable

    574

    0

    0

    574

    System.ValueType

    535

    0

    3

    538

    System.Int16

    0

    345

    139

    484

    System.Collections.IEnumerable

    472

    1

    9

    482

    System.Byte[]

    0

    46

    375

    421

    System.ComponentModel.IComponent

    353

    0

    42

    395

    System.MulticastDelegate

    346

    0

    0

    346

    System.UInt32

    0

    105

    227

    332

    System.IAsyncResult

    13

    3

    315

    331

    System.Byte

    0

    213

    112

    325

    System.UIntPtr

    0

    0

    314

    314

    System.AsyncCallback

    0

    0

    307

    307

    System.Int64

    0

    66

    234

    300

    System.Collections.ICollection

    261

    2

    34

    297

    System.Object[]

    0

    21

    256

    277

    System.Int32&

    0

    0

    267

    267

    System.Array

    233

    0

    32

    265

    System.Attribute

    222

    0

    37

    259

    System.Double

    0

    25

    218

    243

    System.Reflection.Emit.OpCode

    0

    222

    20

    242

    System.Globalization.CultureInfo

    0

    6

    227

    233

    System.Windows.Forms.IWin32Window

    171

    0

    16

    187

    System.String[]

    0

    29

    152

    181

    System.Int32[]

    0

    10

    171

    181

    System.Drawing.Rectangle

    0

    16

    164

    180

    System.Char

    0

    36

    142

    178

    System.DateTime

    0

    40

    134

    174

    System.Exception

    22

    6

    145

    173

    System.IO.Stream

    23

    3

    146

    172

    Figure below shows  Mapping the Basic CLR Types

    System Types Visual Basic .NET Managed C++ C#
    System.Boolean Boolean bool bool
    System.SByte N/A byte sbyte
    System.Int16 Short short short
    System.Int32 Integer long int
    System.Int64 Long __int64 long
    System.Byte Byte byte byte
    System.UInt16 N/A unsigned short ushort
    System.UInt32 N/A unsigned long uint
    System.UInt64 N/A unsigned __int64 ulong
    System.Single Single float float
    System.Double Double double double
    System.Char Char char char
    System.String String System::String string
    System.DateTime Date N/A N/A
    System.Decimal Decimal N/A decimal

    Microsoft has defined a subset of the CTS and features supported by the CLR that all languages must support as a minimum. This subset is known as the Common Language Specification (CLS). For compiler vendors, supporting the CLS means that your language can use any CLS-compliant class library or framework.

    The CTS defines the full set of types supported by the CLR and available internally to any .NET programming language, the CLS defines the subset of the CTS that you must restrict yourself to and a set of rules that compiler and framework developers must adhere to, in order to ensure that their software is usable by all CLR-compliant programming languages.

    Some examples of the rules in the CLS are as follows:

    • A type is CLS compliant if its public interfaces, methods, fields, properties, and events contain only CLS-compliant types or are marked explicitly as not CLS compliant.
    • A CLS Consumer can completely use any CLS-compliant type.
    • A CLS Extender is a CLS consumer tool, and it can also extend (inherit from) any CLS-compliant base class, implement any CLS-compliant interface, and use any CLS-compliant custom attribute on any type, method, field, parameter, property, or event.

    The CLR requires all objects to be created using the new operator, The following statement shows Empl instance creation:

    Empl e = new Empl(“ConstructorParam1”);

    The new operator is encounter by CLR, CLR asks new to perform following operations:

    1. CLR calculates the required bytes by all instance fields defined in the type and all of its base types up to and including System.Object.
    2. It allocates memory for the object by allocating the number of bytes required for the specified type from the managed heap, this allocated memory is initialized to zero.
    3. It initializes the object’s type object pointer and sync block index members.
    4. The type’s instance constructor is called, passing it any arguments specified in the call to new. Most compilers automatically emit code in a constructor to a call a base class’s constructor. Each constructor is responsible for initializing the instance fields defined by the type whose constructor is called. Eventually System.Object’s constructor is called, and this constructor method does nothing but return.

    After new has performed all of these operations, it returns the reference to the newly created object. Also the developer need not to have worry about delete operator anymore, The CLR uses a garbage-collected environment that automatically detects when objects are no longer being used or accessed and frees the object’s memory automatically.

    Casting Between Types

    At runtime, the CLR always knows what type an object is. As we already know that an object’s exact type can identified by calling the GetType method. Because this method is non virtual, it is impossible for a type to spoof another type.

    CLR allows you to cast an object to its type or to any of its base types without requiring you to specify the casting syntax since it is considered implicit conversions. However the developer does need to explicitly cast an object to any of its derived type since such a cast could fail at runtime. The following code shows it why:

    // This type is implicitly derived from System.Object.

    internal class Empl {

    ……

    }

    public sealed class Program{

                public static void Main() {

                // No cast needed since new returns an Empl object

                // and object is a base type of Employee.

                Object o = new Employee();

                // Cast required since Empl is derived from object.

                // Other languages ( such as VB) might not require

                // this cast to compile

                Empl e = (Employee) e;

    }

    At runtime, the CLR checks casting operations to ensure that casts are always to the object’s actual type or any of its base types. For e.g. the following code will compile, but at runtime, an invalidCastException will be thrown :

    internal class Empl {

    ……

    }

    public sealed class Program{

    public static void Main() {

                // construct a Manager object and pass it to PromoteEmployee.

    // A manageer IS-A Object: PromoteEmployee runs OK

    Manager m = new Manager();

    PromoteEmployee(m) ;

    // Construct a DateTime object and pass it to PromoteEmployee.

    // A DateTime is NOT derived from Employee. PromoteEmployee

    // throws a System.InvalidCastException exception.

    DateTime newYears = new DateTime(2010, 1, 1);

    PromoteEmployee(newYears);

    }

    public static void PromoteEmployee(Object o) {

    // At this point, the compiler doesn’t know exactly what type of object o refers to.

    // so compiler allows that code to compile. However, at runtime, the CLR does know

    //what type o refers to (each time the cast is performed) and it checks whether the

    //object’s type is Employee or any type that is derived from Employee.

    Employee e = (Employee)o;

    }

    }

    Because Manager is derived from Employee, the CLR performs the cast and allows PromoteEmployee to continue executing. However, inside PromoteEmployee, the CLR checks the cast and detects that o refers to a DateTime object and is therefore not an Employee or any type derived from Employee. At this point, the CLR can’t allow the cast and throws a System.InvalidCastException.

    If the CLR had allowed the cast the code is unpredictable leading to application crash caused by the ability of types to easily spoof other types. This possibility of conversion is known as Type Spoofing, which is the cause of many security breaches and compromises an application’s stability and robustness. Type safety is therefore an extremely important part of the CLR.

    The “is” & “as” operators for casting objects:

    The C# language is to use the is operator. The is operator checks whether an object is compatible with a given type, and the result of the evaluation is a Boolean : true or false. The “is” operator never throw exception, it always evaluates to any Boolean value.

    Object o = new Object();

Boolean b1 = (o is Object); // b1 is true

b1 = (o is Employee);//b1 is now false

if the object reference is null, the is operator always returns false because there is no object available to verify its existence.

The is operator implemented as follows

if (o is Employee) {

Employee e = (Employee) o;

// Use e within the remainder of the “if” statement.

}

The CLR’s type checking improves security, but it certainly comes at a performance cost, bcoz in the above example CLR verifies the type referred to by the variable o and CLR must ascend the hierarchy tree verifying each base type against the specified type (Employee).

The above statement can be simplified by using “as” operator:

Employee e = o as Employee;

if (e != null) {

// Use e within the ‘if’ statement.

}

In the above code, the CLR verifies whether o is compatible with the Employee type it will be an instance of Employee else it will be null. Then CLR after evaluation returns true if it is compatible and false if e is incompatible because e is now null. similar to “is” operator “as” operator also never emits an exception.

using directive

The C# provide a keyword “using” to avoid using namespace qualified types in the specified region/block  of code, the following e.g. shows how using keyword can be used to avoid by the developer from typing full  qualified type name and enhance readability.

using System.IO;

using System.Text;

public sealed class Program {

public static void Main() {

FileStream fs = new FileStream(….);

StringBuilder sb = new StringBuilder();

}

}

The C# using directive instructs the compiler to try prepending different prefixes to a type name until a match is found.

In the above example, if the C# compiler cannot find the specified type in the source files or in any referenced assemblies, it prepends System.IO. to the type name and verifies the type. If the compiler didn’t find suitable type then it verifies with the next using directive i.e. System.Text. so in this way FileStream is prepended with System.IO and StringBuilder is prepended with System.Text. The compiler automatically expands the code to match correct types during compilation. So using directive really saves a lot of time and improves readability.

Generally any type that is specified in the source code usually starts matching with the core types found in the  Framework Class Library inside MSCorLib.dll

The namespace are usually used for resolving the ambiguity problem faced when two vendors of the third party libraries have same type names, then we should use fully qualified type name composed of namespace and type name.

Creating a namespace is simply a matter of specifying a namespace declaration into your code as follows (in C#)

namespace CompanyName {

public sealed class A {                           //typedef: CompanyName.A

}

namespace X {

public sealed class B { … }                   //typedef Company.X.B

}

}

The comment  on the right of the class definitions above indicates the real name of the type the compiler will emit into the type definition metadata table; this is the real name of the type from the CLR’s point of view.

When the CLR starts running in a windows process, it automatically creates a special type object for the System.Type type defined in MSCorlib.dll. The user-defined type objects are instances of this type and hence their type object pointer members are initialized to refer to the System.Type type object.

Also System.type type object is an object by itself and has a type object pointer member in it, and it’s member refers to itself because the System.Type type object is itself an “instance” of a type object. And System.Object’s GetType method returns the address stored in the specified object’s type object pointer member. In other words the GetType method returns a pointer to an object’s type object, and this is how you can determine the true type of any object in the system.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s