I learned .NET back in 2002 when I read a book about it. However, the book is long gone and .NET has evolved from 1.1, 2.0, 3.0, 3.5. Just around the corner is .NET 4.0. This fast moving set of framework is not easy to pin down.
In this blog, I hope to talk about some of the areas that are not well understood by the beginner and average .net programmer.
System Types
- In a Strongly typed environment, every system type is strongly enforced.
- The .NET framework is a strongly typed environment.
- The .NET framework provides a common set of system type (CTS), derived from the system.object.
Value Types
- Value types are stored in “memory stack” of the application.
- There are 2 types of value types: built-in and user-defined.
- There are 3 types of user-defined value types: Structure, Constant and Enumeration
Reference Types
Reference types are system types that contain a reference to assigned data but do not contain the actual data. The data for reference types is stored in an object, which is stored in a separate memory space.
When you work with reference types, both stack and heap memory are used. Unlike a stack where data is stored in a fixed size, in a heap, blocks of memory are allocated in arbitrary order and the sizes of the blocks are not known until run time.
The data of a reference type is stored on a heap while its reference is stored on a stack. In the code example, a reference to the actual value, Hello, is stored on the stack while the value, Hello, is stored on the heap.
You can create objects on heap memory by explicitly allocating space for the objects by using the new operator. In the .NET Framework, you can create a reference and then point the reference to an object allocated on the heap.
For example, a reference type object, vehicle1, of the Vehicle class is instantiated on the heap by using the new operator.
Multiple reference type variables can refer to the same memory location. In the code example, when you copy the reference type variable, vehicle1, to a new reference type variable, vehicle2, both the variables on the stack reference the same Vehicle class object on the heap.
If one of the reference type variables modifies the value in the memory location, the other variables automatically reflect the modified value.
The .NET Framework provides predefined reference types in the form of classes and interfaces. You use the class and interface keywords to create your own classes and interfaces, respectively. Classes are like templates based on which objects are created.
Classes contain members, such as variables, methods, and properties, which represent the state and behavior of a particular object.
You can create instances of class types by using object-creation expressions. The code example shows the creation of the emp1 object of the class type, Employee class.
In addition to creating your own classes, you can use the predefined classes available in the .NET Framework. Each class belongs to a specific namespace, depending on its purpose.
For example, you use the classes in the System.Windows.Forms namespace to develop Windows applications. You use the classes in the System.Web.UI.WebControls namespace to develop Web applications. You use the classes in the System.Data.SqlClient namespace to develop database applications.
The other predefined reference type available in the .NET Framework is interface. An interface defines standard behavior for classes in a hierarchy. An interface contains properties, events, and function declarations.
However, the functions in an interface are only declared and do not have a body. Interfaces require classes to implement their functionality. The code example defines an interface, IAccount.
For more information about reference types, see “Common Type System” on the MSDN2 Web site.
What would you do when a collection accepts only reference type data and you want to add an integer, which is a value type, to the collection? The .NET Framework provides a solution. You can convert the data through boxing before adding it to the collection. To retrieve that data as an integer from the collection, you can perform unboxing. As a result, boxing and unboxing make it simple for you to use value types when a reference type is expected.
In most cases, boxing occurs implicitly. For example, when passing a value type parameter to a method that accepts a reference type, boxing takes place implicitly. Unlike boxing, unboxing is always performed explicitly. During unboxing, you need to take care that the boxed value can be unboxed only to the same type from which it was boxed. For example, if you box an Int32 to an object, you can unbox the boxed Int32 value only to an Int32 type.
Boxing is the conversion of a value type to a reference type. During boxing, memory is allocated on the heap to store a copy of the value type instance and the internal data required to create a valid reference object. After the memory is allocated, the value type instance is copied to the newly allocated heap memory and the address of the object is returned. This address is now a reference type.
The following code example shows the conversion of an integer variable a to an object o by using boxing. The value stored in the variable a is changed from 100 to 200. The example shows that the object o keeps the original value, 100.

Dim a as Integer = 100
Dim o as Object = a
a = 200
Console.WriteLine("The value-type value = {0}", a)
Console.WriteLine("The object-type value = {0}", o)
int a = 100;
object o = a;
a = 200;
Console.WriteLine("The value-type value = {0}", a);
Console.WriteLine("The object-type value = {0}", o);Unboxing is the opposite of boxing. It is the explicit conversion of a reference type to a value type. Unboxing retrieves a reference to the value type contained within an object. An unboxing operation involves checking the object instance to ensure that the object instance is a boxed value of the given value type. The value from the instance is then copied into the value type variable. Unboxing returns a pointer to the data within a boxed object and does not create a copy of the data.
The following code example implements unboxing. The variable o is explicitly converted back to the integer a.
Dim a as Integer = 1
Dim o as Object = a
a = 100
Console.WriteLine(a)
a = CInt(o)
Console.WriteLine(a)
int a = 1;
Object o = a;
a = 100;
Console.WriteLine(a);
a = (int) o;
Console.WriteLine(a);Generics is a new concept in the .NET Framework. Generics allow system types such as classes and interfaces to have methods that do not have a specific type in a type-safe manner. You can understand the best use of generics while creating a collection by using generics. For example, you need a collection class that you can use to create type-safe collections of the Teacher and Student objects. In other words, the Teacher collection should consist of objects of only the Teacher class and the Student collection should consist of objects of only the Student class. In earlier versions of the .NET Framework, collections store objects as instances of the System.Object type. As a result, you can use a single collection object to store objects of any type. This compromises type safety and involves a performance overhead because of the need for implicit and explicit type casting while adding or retrieving objects from the collection. The .NET Framework 2.0 provides generics that you can use to create type-safe collections for both reference and value types.
The .NET Framework 2.0 provides a number of generic collection classes in the System.Collections.Generic and System.Collections.ObjectModel namespaces. Using these classes offers various advantages, as follows:
- Reusability: A single generic type definition can be used for multiple scenarios in the same code, without any alterations. For example, consider that you are writing code to add two numbers. Prior to the concept of generics, the best way to handle this was to create overloads of the Sum method with different data types and return the value. However, now you can create a generic method to add these two numbers and call it with values of the required data type.
- Type safety: Generic data types provide better type safety, especially in situations where collections are used. In a Collection class, when you add objects, the compiler does not check the type of the object you are adding. As a result, the cast at run time may fail. On the other hand, with generics you can define the type of objects to be passed to a collection and the compiler at compile time makes sure that only those objects are passed. This improves type safety and reduces the possibility of type casting errors.
- Performance: Generic types perform better than normal system types because they reduce the need for boxing, unboxing, and type casting the variables or objects.
The following code examples show how to implement generics in Visual C#. The code shows the reusability and type safety advantages of using generics. The code example defines a generic class called CommonData that is used to create two objects – one to store a string value and the other to store a float value. The CommonData class ensures type safety by accepting the required type in its constructor. This implies that the value specified for an object of the CommonData class must be of the same type as that specified while creating the corresponding object.
| Implementation | Code Example |
|---|---|
class Program{ static void Main(string[] args) { CommonData<string>name = new CommonData<string>(); name.Value = ".NET Framework"; CommonData<float>version = new CommonData<float>(); version.Value = 2.0F; Console.WriteLine(name.Value); Console.WriteLine(version.Value); } } public class CommonData<T> |
As a developer, you will frequently find yourself working with a value type that does not have a defined value. For example, you need a null value to distinguish a field in a database that either has been assigned or has not been assigned a value. If you assign the keyword Null, to a value type variable, the compiler will throw an error. The .NET Framework 2.0 provides a new feature called the Nullable data type that you can use to assign null values for value type variables. A Nullable data type can store null values in addition to the normal set of values. For example, if you define a Nullable int data type, it can store either a Null value or an integer value.
All Nullable types are instantiated using the generic System.Nullable structure. This makes using the Nullable type as easy as using a normal data type because most of the properties and methods are implemented through the System.Nullable structure. This structure combines a value of the underlying type with a Boolean null indicator.
The following job aid shows and describes code examples for declaring Nullable types in Visual Basic and C#.
The following table shows an implementation of Nullable types in Visual C#. The code example defines an Employee class that consists of member fields to store information on employee name, marital status, and date of anniversary. The class defines the field for date of anniversary as Nullable and ensures that the field stores a null value if the employee is unmarried. While retrieving values from these fields, the class uses the HasValue property, which is available in Nullable types. This property helps determine whether the Nullable field has a value assigned to it or it contains a null value.
| Implementation | Code Example |
|---|---|
|
Attributes
An attribute is a declarative tag. You can use an attribute to convey information to the runtime about the behavior of programmatic elements such as classes, enumerators, and assemblies. You can also think of an attribute as an annotation that your program can store and use. In its simplest form, an attribute is an extended way to document code. You can apply attributes to many elements of the source code. Information about the attributes is stored with the metadata of the programmatic elements with which they are associated.
You can either use the predefined attributes that the .NET Framework provides or create custom attributes by using the System.Attribute class. The .NET Framework compiler creates attributes when you declare instances of special classes that derive from the System.Attribute class.
In C#, you specify attributes between brackets ([ and ]). The following code example shows how to declare System.ObsoleteAttribute, which marks code as obsolete. This attribute is applicable only to the Subtract method. The string This function is obsolete is passed to the attribute. This ensures that the compiler generates a warning when the Subtract method is called. The warning displays the string specified with the attribute to describe the warning.
public class ObsoleteAttributeExample
{
[Obsolete("This function is obsolete")]
public static int Subtract( int a, int b)
{
return (a-b);
}
public static void Main()
{
int result = Subtract(9,2);
}
}In object-oriented programming, you will often need to define common behavior and attributes for related classes while leaving the implementation of the behavior and attributes up to each class. A good example of this is creating a drawing application that draws shapes such as rectangles, squares, and triangles. The classes used to define these shapes need to have common behaviors and attributes such as Draw and Area. To achieve this, you can use an interface such as IShape to define the draw behavior and its attributes, such as Area, Height, Width, and Color. However, you can leave the implementation of the interface up to each class because the area of a rectangle is calculated differently than the area of a triangle.
As an object-oriented programmer, you can use interfaces to achieve the goal of abstraction while simultaneously leaving the implementation to the classes that implement them. Interfaces are nothing more than definitions of contracts to which other classes and structures adhere. An important benefit that interfaces provide is multiple inheritance. Although a class cannot inherit from multiple base classes, a class can inherit from multiple interfaces. For example, a class representing a rectangle needs to inherit from a base class called Shape and at the same time, needs to have the printing functionality from a type called Printable. In this case, you can use interfaces to incorporate the behaviors of multiple types into one class. You need to take care that while implementing multiple interfaces in a class, the class must provide the implementation of all the methods and properties defined in each interface.
You can use the predefined interfaces available in the .NET Framework to perform common programming tasks, such as comparing reference types for equality, converting implementing classes from one type to another, or meeting locale-specific formatting requirements.
In the .NET Framework, you need not destroy objects from memory yourself. Instead, a special component of the .NET Framework run-time environment known as garbage collector, manages the release of object memory on the heap.
The garbage collector periodically looks for unused objects on the heap and de-allocates their memory. This is known as finalization of an object.
At the time of finalization, the Finalize method of the object is called automatically. However, there is no way to determine the exact timing of this finalization process. You cannot call the Finalize method explicitly.
However, some unmanaged resources, especially resources such as Graphical Device Interface or GDI and file handlers carry a lot of memory overhead. Ideally, you would want to release these resources as soon as you finish with them rather than waiting for the finalization process to occur.
In the .NET Framework, you can release unmanaged resources explicitly by implementing the IDisposable interface. This interface defines the Dispose method. You use the Dispose method to release unmanaged resources explicitly.
Though the garbage collector does not call the Dispose method, this method works in conjunction with the garbage collector.
The code example shows the implementation of the IDisposable interface in a class named CustomerDataAccess. The class implements the Dispose method defined by IDisposable to provide a way to release resources explicitly.
The Dispose method releases the resources with the help of a supporting custom-defined ReleaseResource method and removes the current object from the finalization queue. This relieves the garbage collector from calling the finalizer of an object after the object is disposed.
The class also calls the ReleaseResource method in the finalizer or destructor to ensure that the garbage collector releases the unmanaged resources of the class, even if the Dispose method is not called explicitly.
For more information about garbage collection, see “Garbage Collection” on the MSDN2 Web site.
C#
public class CustomerDataAccess: IDisposable
{
protected virutal void Dispose(bool disposing)
{
if(disposing)
{
// call dispose on any objects referenced by this object
}
// release unmanaged resources
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
~CustomerDataAccess()
{
this.Dispose(false);
}
}
IComparable interface example:
public class Employee : IComparable
{
protected int id;
protected string name;
public int CompareTo(object obj)
{
if(obj is Employee)
{
Employee temp = (Employee)obj;
return this.name.CompareTo(temp.name);
}
throw new ArgumentException("Object is not an Employee");
}
}
IEquatable interface example:
public class Result : IEquatable<Result>
{
protected string strPlayer;
public string Player
{
get { return strPlayer; }
set { strPlayer = value; }
}
public override bool Equals( Object obj )
{
return Equals(obj as Result);
}
public bool Equals(Result other)
{
if(other == null)
{
return false;
}
return (other.Player == this.Player);
}
}{
bool _agree;
DateTime IConvertible.ToDateTime(IFormatProvider provider)
{
throw new InvalidCastException("Cannot cast to DateTime");
}
//... other IConvertible Methods
}
Visual Basic 











