Introduction
In this article, I will learn about and use the new features in C# 2.0 added by the Microsoft designers, such as generics, anonymous methods, partial types, static classes, nullable types, and limiting access to properties.
Generics
In .NET 1.x, all of the collections were declared to hold instances of System.Object, so it's not type-safe. An even bigger problem, it's when you add different types, including to boxed values, to the same collection, and later you need to access the objects, you should keep a record of the type for every object in the collection.
In .NET 2.0, it is eliminated this kind of problems using a new library of strongly type collections (System.Collections.Generic namespace) and a new C# language construction for expressing and declaring semantically generic objects, so the programmer could find type-safe bugs in compile time rather than in runtime time.
A generic provides a way for the developers to define type parameters for methods arguments and type definitions.
Let's define a custom generic class. Just add the angle brackets and the type parameters inside them to the class definition, and declare generic attributes inside the classes.
See an example below.
Let's define the concepts for a generic linked list using generic nodes.
public class Node<T>
{
private T m_objGenericObject;
private Node<T> m_objNext;
//Ctor
public Node(T objGenericObject)
{
this.m_objGenericObject = objGenericObject;
}
// Properties
public T Data
{
get
{
return this.m_objGenericObject;
}
}
//Methods
public Node<T> Next
{
get
{
return this.m_objNext;
}
}
public void Append(Node<T> objNewNode)
{
if (this.m_objNext== null)
{
this.m_objNext = objNewNode;
}
else
{
this.m_objNext.Append(objNewNode);
}
}
}
Listing 1.
The above definition could be read as Node of type T. Now it's possible to create a deal of linked list,
Node<String> objNode=new Node<string>("John Charles");
Node<String> objNext=new Node<string>("olamendy");
objNode.Append(objNext);
Node<int> objNode=new Node<int>(3);
Node<int> objNext=new Node<int>(4);
objNode.Append(objNext);
Listing 2.
Let's a set of generic collection classes defined by.NET designers and the non-generic counterpart. It's possible to create collections of any type that the compiler recognizes.
Generic class |
Non-generic counterpart |
Meaning |
Collection<T> |
CollectionBase |
The basis for the collections |
Comparer<T> |
Comparer |
Compares two objects for equality |
Dictionary<K,V> |
Hashtable |
A collection of name-value pair |
List<T> |
ArrayList |
A dynamic resizable list of items |
Queue<T> |
Queue |
FIFO list |
Stack<T> |
Stack |
LILO list |
List<string> arrString = new List<string>();
arrString.Add("john charles");
List<int> arrInt=new List<int>();
//No boxing!
arrInt.Add(1);
//No unboxing!
int nValue = arrInt[0];
Listing 3.
Anonymous methods
Anonymous methods allow you to define method blocks inline. In general, you can use anonymous methods anywhere you can use a delegate. This can greatly simplify registering event handlers.
For registering the delegate which invokes the method, we used to write the following code.
class Program
{
static void Main(string[] args)
{
SomeType t = new SomeType();
t.SomeEvent += new SomeDelegate(MyEventHandler);
}
// Typically only called by the SomeDelegate object.
public static void MyEventHandler(object Sender, EventArgs ex)
{
//Code implementation
}
}
Listing 4.
Now you may write the same business logic as follow,
class Program
{
static void Main(string[] args)
{
SomeType t = new SomeType();
t.SomeEvent += new delegate(object Sender, EventArgs ex)
{
//Code implementation
}
}
}
Listing 5.
Partial Types
In previous versions of C# the entire definition for a class had to be in a single file. Now, using the partial keyword, you can split your class across more than one file. It allows to have different team members working on different parts of the class, and IDE such as Visual Studio 2005 can separate the designer-generated code from your own user code.
There are some features about partial types, such as:
- All partial type definitions must be modified with the partial keyword and must belong to the same namespace and the same module and assembly.
- The partial modifier can appear only before the class, interface, and struct keywords.
- Access modifiers (public, private, etc.) must match all the partial types of the same class.
Static classes
The purpose of a static class is to provide a set of static utility methods scoped to the name of the class. When a class has been defined as static, it is not creatable using the new keyword, and it can contain only static members or fields (if this is not the case, you receive compiler errors).
This type of construction is very useful for defining utility classes.
Let's define an utility class for converting types to System.String type.
public static class StringUtility
{
public static string Convert(int nValue)
{}
public static string Convert(DateTime dtValue)
{}
public static string Convert(float ftValue)
{}
}
Listing 6.
Nullable Types
With new nullable types, you can assign value types a null value. This can be tremendously powerful, especially when working with databases where the value returned might be null; without nullable types you would have no way to express that an integer value is null, or that a Boolean is neither true nor false.
Semantically when a value is null, it means that no object references exists to this alias (value name). In order to follow the principles of .NET where all entities are object (String, int, float, Person class), it was previously impossible to express that there is no object associated to an alias integer.
You can declare a nullable type as follows: System.Nullable<int> nAge; or, you may use simplified form int? nHeight.
You can check whether a nullable variable is null in two ways as well. You can check like this: if(nHeight.HasValue) or like this: if(nHeight!= null).
To see the actual value of the variable use the following construction nHeight.Value.
Limit Access Within Properties
It is now possible to restrict the accessibility level of the get and set accessors within a property using access modifiers. Usually you would restrict access to the set accessor and make the get accessor public.
In the following listing, it's granted permission to everyone for reading Customer.Name and Customer.ContactInfo properties, and it's granted permission to everyone for changing only Customer.ContactInfo property. But it's granted permission only to friend entities for changing Customer.Name property.
public class Customer
{
private string m_strName;
private string m_strContactInfo=null;
public Customer(string strName)
{
this.m_strName=strName;
}
public string Name
{
get
{
return this.m_strName;
}
protected set
{
this.m_strName=value;
}
}
public string ContactInfo
{
get
{
return this.m_strContactInfo;
}
set
{
this.m_strContactInfo = value;
}
}
}
Listing 7.