Last modified on 21 February 2011, at 17:20

.NET Development Foundation/System Types Topics


System types and collections: Topics


TopicsEdit

Programs, objects and methodsEdit

.NET is essentially a set of tools to build and execute computer programs. A computer program is a set of instructions given to a computer that executes those instructions automatically to manipulate some data. .NET follows the object-oriented paradigm which means that the instructions and data are grouped around ‘’objects’’ that represent things or concepts. Objects that share the same instructions and manipulate the same kind of data are grouped into the same type.

An object-oriented program is the definition of a series of types. For each type the kind of data and the instructions are specified. The instructions are grouped inside methods. One of the available instructions is the creation of an object of a defined type. Another kind of instruction is the request to execute a method associated with an object. This is called invoking a method. When a method is invoked its instructions are executed until the method terminates (returns). When the method returns the calling (invoking) method executes its next instruction (statement).

When you start the execution of a program the system creates a first object and invokes a method of that object (this is called the main method). Inside that method other objects are typically created and methods of those objects are invoked. Those methods will invoke other methods and create other objects and so on. Gradually the called methods will return and the '’main’’ method will eventually finish, marking the end of the program execution.

System typesEdit

This section will be obvious for experienced object oriented developers but some of the specific objectives of the exam are directly related to the type system.

Types are a way to classify the concepts or objects of a language. The way this classification is organized is called a 'type system'. The types themselves can also be categorized in different ways by the type system.

The first way to categorize types in .NET is to make a difference between types that are part of the framework class libraries (System types) and types that will be constructed by the developer (custom types).

Writing object oriented programs can be seen as the process of defining one or more custom types. Those types are then packaged in some kind of execution unit (Assemblies in the case of .NET). The assemblies are compiled and then executed starting at some entry point that will be a specified method of one of the custom type.

Those custom types use:

  • System types to execute "pre-programmed" instruction sequences
  • other custom types

System types are also packaged in assemblies. The custom assemblies must reference the system assemblies in order to use the System types.

There are other ways to categorize types in .NET. One of them is by the way the objects created based on those types are mapped to the computer memory. This will give us Value types and Reference types.

Another way is by Reflection category (Class, Value types, Interfaces, Generics, etc.).

Yet another way is to distinguish the types that are directly supported by the runtime (built-in types) from those defined either in the class libraries or custom.

Those categories can also be intersected with one another, that will give us such things as "Built-in value types" or "System interfaces". Stay alert of the categorizations used when you encounter such combinations.

Namespaces are a way to organize types so that they can be more easily found. See this for a discussion on namespaces.

In the context of namespaces the System types are the types included in the System namespace or one of its sub-namespace and Custom types (non system types) should use other namespaces.

For a peek at how Microsoft describes the .NET type system see MSDN. And then for an overview of the class libraries (System types) see MSDN.

Most of the exam is in fact based on how to use the common parts of the type libraries (System types). This is why the list of exam objectives (and this book TOC) is so long.

Hello worldEdit

For the newcomers to .NET, you may want to take a little break from the concepts here and make sure that you see how the concepts discussed so far are used in a very simple example. The next concepts are not that trivial. We will put such an example here.

Value typesEdit

Value types represent one part of the Value / Reference classification of the type system.

An instance of a value type directly contains its data (value). For example an Int32 local variable has its memory allocated directly on the stack.

The value types are themselves split in 3 categories:

  • The built-in value types
  • User-defined value types
  • Enumerations

Remember that built-in types are the types directly supported by the runtime.

They are the building blocks of any program because they are what the machine instructions ultimately act upon. The rest are essentially compositions of those types.

All of the built-in types are value types except for Object and String.

The built-in value types consist of

  • the integer types (Byte, SByte, Int16, Int32, Int64, UInt16, UInt32 and UInt64)
  • the floating point types (Single and Double)
  • the logical type (Boolean)
  • other (Char, Decimal, InPtr and UInPtr)

All built-in value types are defined in the System namespace (ex. System.Int32) and have keywords that represent them in the VB and C# (ex. int in C#, integer in VB.NET) except for InPtr and UInPtr.

Side note

We noted above that type classification can be quite confusing sometimes. As an example note that System.DateTime is presented as a built-in value type in the Training kit (page 5) and this is not identified as an error in MSDN KB. It is not a built-in type according to the official specification (page 19). The confusion comes from the fact that the Training kit does not make a clear difference between System types (in the class libraries) and Build-in types (basic types dealt with directly by the runtime).

The point here is not to be fussy or depreciate the work done by the authors of the training kit. We just want to note that it may be worth to take a few minutes to clearly separate the concepts before heading to the code.

All value types derive from System.ValueType either directly for built-in and user-defined or indirectly through System.Enum for enumerations.

Enumerations are a way to name a set of values of an underlying integer type (signed or unsigned). As restrictions of an integer type they act as their underlying type.

User-defined value types and Enumerations both include System types and Custom types.

An example of a System user-defined value-type would be System.Drawing.Point used in drawing.

You build custom value types using specific language constructs (struct in C#, Structure in VB.NET).

An example of a System enumeration would be the System.Data.CommandType enumeration that specifies if a table is a text command, a call to a stored procedure, etc.

You build custom enumerations using specific language constructs (enum in C#, Enum in VB.NET).

For examples and notes on using value types see this section. See also examples of building and using user-defined value types.

Reference typesEdit

Reference types represent the other part of the Value / Reference classification of the type system.

Contrary to value types, an instance of a reference type does not directly contain its data (value), but instead contains some kind of a reference to the memory location of that value. For example a String local variable has memory allocated on the stack for a reference to the contained string, not the string itself. In that case the string itself will be allocated on the heap and garbage collected (more on that later).

Two built-in types are considered reference types: Object and String and they are also directly referenced in the System libraries (ex. System.String) and have their own constructs in the .NET languages (ex. string in C#).

The reference types are themselves split in four categories:

  • pointers
  • arrays
  • classes
  • interfaces

We will not talk about pointers in this book because no exam objective references them.

The next three sections will present arrays, classes and interfaces.

For a comparison of value / reference types try this.

For other notes and an example of using reference types see this.

ArraysEdit

An array is essentially a group of objects, usually of the same type, than can be accessed via an index.

For a general discussion of arrays you can see the Wikipedia article (on the right) or go to the Wikibook on data structures.

Arrays once were one of the most used features of programming languages because you can go from one item in the array to the next in a very efficient way (you can see C pointers and arrays if you want more details on this). Today the availability of much more “computer power” has moved the focus from arrays to collections. The two main problems of arrays are:

  • They are fixed length
  • They support only one internal organization

Collections address most of the shortcomings of arrays (at a cost though). Arrays can still be considered for a fixed group of objects that have to be efficiently manipulated.

We will have some examples in the using arrays section.

ClassesEdit

Classes are themselves split in three:

  • user-defined classes
  • boxed value types
  • delegates

Boxed value types will be discussed a little later in the boxing / unboxing section.

Delegates will also be covered later in their section.

User-defined classes are the basic realizations of the object oriented concepts.

Obviously a lot can be said about classes but since no exam objective reference to them we will assume that the reader is already familiar with the concept and have already worked with them (in .NET or elsewhere).

You can have System classes (hundreds and hundreds of them) and custom classes (where you will code the essential of your program logic).

We have examples of using and building classes.

InterfacesEdit

If you want to get really confused on what exactly is object-oriented programming just check the Wikipedia article on Polymorphism :-).

The idea is just to be able to have a single type of reference to the "common part" of different types that have "something in common".

Rectangles and Ellipses are both "Shapes" so how can we get part of a program to manipulate "Shapes" without knowing about the specific of Rectangles or Ellipses. In such a situation we say that Shapes are polymorphic (literally they can take many forms).

In object-oriented programming you get this behavior with inheritance. A reference to a parent class (Shape) will manipulate child objects (Rectangles or Ellipses) without problems.

Interfaces were developed to get the same behavior in the context of component-based computing where you don't have access to the source code so you cannot use inheritance. The interface construct was the basis of all component communication in COM.

An interface is a contract to implement a set of methods in a clearly defined manner (method signatures). If you know that a class implements an interface you know that you can use any of the defined methods.

From that definition it is easy to imagine having a reference to an "interface instance" from which you can call any of the interface methods. So easy in fact that the framework provides just that.

Now suppose that instead of a Shape being a parent of Rectangle we just have a Shape interface that is implemented both by Rectangle and Ellipse. If I have a reference to a Rectangle object, I will be able to "cast" it to a reference to a Shape interface and pass it to the part of the program that knows only about "Shape objects".

This is exactly the same polymorphic behavior that we had via inheritance.

The class libraries rely heavily on interfaces and a clear understanding of the concept is essential.

An interesting problem with interfaces compared with inheritance is that you do not get a default implementation (virtual parent methods) so you have to recode everything. The techniques and tools do exist here but there is always some extra work to do. More on that with generics...

We have examples of using and building interfaces. Some interfaces of the System namespace are shown in the standard interfaces section.

AttributesEdit

We now take a short break of our discussions on types to talk a little bit about attributes. First of all attributes are not types. They are elements of information that are added to a program element (assemblies, classes, method, etc.) to qualify it outside of the normal code associated with that element.

Those added "attributes" serves at doing manipulations of the underlying program element without having to modify the execution logic of the element.

Why would we ever want to manipulate program elements outside execution code? The short answer here is that object oriented concepts do not easily deal with what is known as cross-cutting concerns, or aspects of a program that apply to all or most classes. Examples of such aspects are security, persistence, serialization, etc. Instead of modifying every class to add serialization logic (which has nothing to do with business rules) you can add serialization attributes to the class to direct the serialization process of those classes without changing the business logic (execution code).

A number of functionality of the framework rely on attributes to add information to program elements. The attributes are kept by the compilation process and associated with their qualifying element in the assemblies. They are analyzed programmatically at run time using reflection to adjust the behavior of "cross-cutting" functionalities.

As for types we have System attributes (part of the framework) and Custom attributes (built by the developer). The development of Customs attributes will be treated in the reflection section. Until then we will limit ourselves with System attributes and focus on defining them and knowing how they will determine the behavior of framework.

The attributes usage section will give some example of how simple System attributes can be used.

Let's note here that attributes are not the only way to add "non-execution" information to a program to modify its behavior. XML configuration files for example can be used to specify parameters that will affect program execution. We will talk about those in the Configuration section of this book.

See MSDN for the discussion of attributes in C#.

CollectionsEdit

A collection is a grouping of objects. The framework has many types of collections that cover most of the situations where you would process objects as a group. Knowing these collection types will save you the time to "re-code" equivalent logic and will keep your program more maintainable.

Unlike arrays collections come in many flavors each with its specific internal organization. Each type of collection is related to a specific type of problem. We will point out the types of problems when we give examples of each type of collections.

We have two sections with collections examples:

The major drawback of collections is that in "real life" objects that are grouped together usually have some characteristics in common (they are the same type, have a common parent type or support a common interface). So most of the time we know "more" about the objects than merely their organization. Collections do not allow us to use that knowledge to validate the objects passed to the collection or code logic applicable to all objects of the collection (without casting and exception handling that is).

Generic collections where introduced in version 2.0 of the framework. They solve this problem while keeping the other advantages of collections. For that reason they should be used whenever possible instead of "plain" collections.

Some external links on collections are GotDotNet and AspNetResources

See MSDN for a general discussion on arrays, collections and data structures.

GenericsEdit

Generic programming or the use of parameterized types is not an object oriented concept. In that sense Generics are a bit like attributes, they were added to main stream object-oriented platforms to take care of situations that are not easily covered by object-oriented techniques.

To start our discussion please read the following interesting linked article (annexes) that introduces the concept of generics in the context of generic collections. This external tutorial does the same.

The concept of Generics is interesting because it applies the concept of generalization to types. Types themselves are generalizations of objects (the basis of object-oriented programming). So here we start manipulating types, namely having types as parameters.

The actual type will be obtained when you substitute the parameter type with a specific type (For example when you declare a variable of that type).

// C#
List<int> myIntList = new List<int>()

'// VB.NET
Dim myIntList As New List(Of Integer)

In the .NET framework this substitution is done during the second compilation (the just-in-time compilation from CIL to machine code). Said differently, generics are supported by the CIL and are thus part of the framework itself, not of the language used (ex. C#).

For more coverage on generics see MSDN.

We have examples of using and building generic types.

For examples of generics collections see the using generic collections section.

ExceptionsEdit

This is how you throw a general exception:

// C#
throw new Exception("This is an exception.");

'// VB.NET
Throw New Exception("This is an exception.")

This is how you handle an exception:

// C#
try
{
   throw new Exception("This is an exception.");
}
catch (Exception e)
{
  Console.WriteLine(e.Message);
}

'// VB.NET
Try
  Throw New Exception("This is an exception.")
Catch ex As Exception
  Console.WriteLine(ex.Message)
End Try

Events and DelegatesEdit

For the “official” discussion of events and delegates see MSDN. What we will do here is a little more general discussion of the many concepts involved.

The first concept is delegation or the idea that part of the functionality of a class can be done “elsewhere” in the program, it can be delegated. The important benefit of delegation is that the “delegating” object does not have to know how the delegated functionality is actually implemented. One way to “delegate” is to define an interface. If an object has a reference to an interface it can delegate part of its functionality to the object implementing that interface without knowing much about that object.

.NET 2.0 defines another reference type called a delegate that implements the delegation pattern in a slightly different way. A delegate is a type that defines a reference to a single function that must have the same signature as the delegate definition. A signature is a description of the type of the function parameters and its return type. Like a class you can create an object of that type. The object created is a reference to a function that can be assigned (set the reference to a specific function), be passed as parameter or executed (the function that is referenced is actually executed). Unlike an interface, a delegate defines only one function and a delegate instance can be created directly (no need to have another class implementing the interface).

Most delegates in .NET derive from a multicast delegate. A multicast delegate is a delegate that keeps a list of references to functions having the same signature as the delegate definition instead of a single reference. You can add a reference to a multicast delegate using the += operator. When you execute a multicast delegate each function referenced is executed in turn.

If an object is from a class that implements a multicast delegate member, then if you have a reference to that object you can "add" to the multicast delegate a reference to a function of your choice. If the object decides to "execute" its delegate member then the function from which you added a reference will be executed.

This multicast delegate member is precisely what an event is in .NET. This is why events and delegates are almost always discussed together.

The procedure to define an event is:

  • You define a delegate type as a reference to functions having a specific signature
  • You add an Event member to a class and associate it with the delegate type you just define, this instantiate a multicast delegate associated with the event
  • When you have a reference to an object of the class you can add a reference to any method that has the same signature as the delegate type.
  • When the object raises the event, the associate multicast delegate is executed which triggers the execution of the referenced function.

Most of the time you will add a reference to one of your own method. When the referenced object fires its event it actually executes one of your methods without knowing it. This setup is an implementation of the publish / subscribe pattern. You subscribe to an event, signaling that you want to be informed when a specific "event" happens. Many object can subscribe to the same event. When the referenced object fires its event it "publishes" a message that the event has effectively happened in the form of a function call to all the "registered" functions.

So any class can define events. Many system classes define events to communicate to your code the fact that "something" happened in the executing environment (the mouse moved, a key was pressed, a message was received on a socket, etc.). Constructing a program that "waits" for events to happen and then react to them is called event programming. Most online applications and services follow this design. We will discuss in more details the way system events are catched in the section on multithreading.

For examples of events and delegates see this section.