Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Types and Collections in C# 3.0 .NET

0.00/5 (No votes)
13 Jul 2010 1  
Defines collections and types and explains their use in the C# 3.0 language.

Abstract

Collections and types are a safe way to ensure that the Developer’s code is strongly typed.  A strongly typed piece of code avoids the need for casting operations and provides for compile time errors instead of runtime errors.  Using collections and types in a .NET assembly reduces the time and memory required to transform inputs.  Collections also have the added attraction of being enumerated, and properties such as the number of items and a collection iterator allow for simple implementation.  Anupam Banerji explains their use.

Introduction

.NET developers must often convert data types.  A string from a textbox control or a console input may have to be converted into an integer, double, or a Boolean value type. Such a type may be then used in a collection of objects.  Often custom reference types must be created in order to store various properties of an object.  A large number of these objects might be needed for sorting or storing. Types and collections provide an efficient implementation technique.

Types

A type is a piece of data stored in memory.  There are two such types.  Value types are in-built .NET data types.  For example, integers, doubles, decimals, floats, Booleans, characters, date/time and bytes are .NET data types.  Value types are stored in an area called the stack, this is a Last In First Out (LIFO) storage space from the days of DOS.  Access to the stack is quick, and the types stored are indexed.  Reference types are user defined types (.NET supplies many pre-built reference types) stored in an area of memory known as the heap.  Objects, strings, arrays and streams are .NET supplied reference types.  All types (value and reference) are derived from the object type, the simplest data type in the .NET Common Type System (CTS).

A user defined type may contain both CTS value and reference types.  A simple user defined type is the enumeration:

private enum ExampleEnum
{ 
    firstItem, 
    secondItem 
} 

The enumeration above provides an integer based index which allows the Developer to assign names to integer indexes.  Using enumerations makes implementation easier and safer.

The simplest user defined type is the structure:

struct ExampleStr 
{     
    public string str; 
    public object o; 
    public Exception e; 
} 

The structure is then used by declaring a variable of the type:

ExampleStr ex = new ExampleStr(); 

A third way to hold data is the class type.  Classes are objects containing types and other code and are a key building block in object oriented design patterns.  Classes are a reference type; they are instanced the same way as structures and have constructors.  Classes are different to structures in that they can derive base classes, implement interfaces, and in .NET, control their disposal through implemented garbage collection.

Collections

A collection is a group of types.  Several types of collection exist.  These are array lists, queues, stacks, string collections and bit arrays.  There are also generic collections such as SortedList<T,U>, Queue<T>, Stack<T> and List<T>.  When the underlying data type implements the IComparable interface, the generic collection may be sorted through implemented methods.

Casting Operations

Why are types and collections necessary?  It is possible to cast objects as CTS types and recast them into useful types when required.  There are a few reasons why instancing common types and recasting them into required types should be avoided.

The first reason is the amount of time required to cast a CTS type into another.  A boxing operation occurs when a value type is cast into the object type.  An un-boxing operation occurs when an object type is cast into a value type.  Boxing and un-boxing operations result in a performance penalty.  If there are thousands of such operations in your code, then there will be a noticeable performance drop.

There is another issue.  An incorrectly boxed or unboxed object throws an invalid cast exception during runtime.  Correctly declaring the inputs into the method during design would result in a compile time (and not runtime) error.

A Quick Example

We will now build a simple sort-able class and a generic collection to demonstrate some of the concepts introduced in this article.  We create a class that implements the IComparable interface.  The class implements the CompareTo() interface method.

using System.Collections;
using System.Collections.Generic;

public class Sortable : IComparable 
{
    private string name
    {
        get;
        private set;
    }

    private int price
    {
        get;
        private set;
    }
    public Sortable()
    {
    }

    public void Add(string _name, int _index)
    {
        name = _name;
        price = _index;
    }

    #region IComparable Members

    int IComparable.CompareTo(object obj)
    {
        return price.CompareTo(((Sortable)obj).price); 
    }

    #endregion
}

The class adds an item with a name and an index.  The interface implementation compares the prices and returns an integer with the value of zero if the prices are identical, less than zero if the price is less than the compared price, and greater than zero if the price is greater than the compared price.  The object being compared must be un-boxed into the Sortable class before any comparison between the prices can be made.  This is expected; the implemented interface cannot account for custom objects.

We now create the main function.  The collection of items is stored in a List<T> object:

List<Sortable> list = new List<Sortable>(); 

We then add items to the generic list collection.  Once this collection has been filled, we can sort the list:

list.Sort(); 

Or reverse the sort order:

list.Reverse(); 

The sorting functions use the implemented IComparable interface in the Sortable class to compare the sort values.  We could easily sort by the name property:

#region IComparable Members

int IComparable.CompareTo(object obj)
{
    return name.CompareTo(((Sortable)obj).name); 
}

#endregion

The List<T> collection contains several generic methods of the ArrayList object.  We can also map the Stack<T> generic to the Stack object and the Queue<T> collection to the Queue object.  Stacks and queues are not sorted.  A stack is a Last In First Out (LIFO) model, and the Queue is a First In First Out model (FIFO).  Stacks would use the Push() and Pop() methods to add and remove items, and queues would use the Enqueue() and Dequeue() methods to add and remove items.

Conclusion

Types and collections are a powerful, safe and effective way to process large groups of data.  Implementing type-safe code results in fewer runtime errors and allows the Developer to reuse pre-built .NET interfaces and methods.  There are fewer implementation bugs and the code algorithms are widely understood.  Using types and collections is therefore recommended as a standard development practice.

A PDF version of this technical article may be downloaded from the Coactum Solutions website at http://www.coactumsolutions.com

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here