Overview
Generics provide the solution to a limitation in earlier versions of the common language runtime and the C# language in which generalization is accomplished by casting types to and from the universal base type Object. By creating a generic class, you can create a collection that is type-safe at compile-time.
The limitations of using non-generic collection classes can be demonstrated by writing a short program that makes use of the ArrayList collection class from the .NET Framework base class library. ArrayList is a highly convenient collection class that can be used without modification to store any reference or value type.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Generics
{
class GenericList
{
static void Main(string[] args)
{
// The .NET Framework 1.1 way to create a List
ArrayList list = new ArrayList();
// Add an integer to the List
list.Add(3);
// Add a string to the List. This will compile,
// but may cause a Runtime Error!
list.Add("This will trigger a Runtime Error!");
// We cast to int, but this will causes an InvalidCastException
// when encounter a string in the List.
int t = (int)list[1];
t = 0;
foreach (int x in list)
{
t += x;
}
// The .NET Framework 2.0 way to create a List
List<int> list1 = new List<int>();
// No boxing, no casting
list1.Add(3);
list1.Add(1);
// Compile-time error:
list1.Add("This will trigger a Compile-time Error!");
// Get the values from the List, no casting
int t1 = list1[1];
t1 = 0;
foreach (int x in list1)
{
t1 += x;
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Generics
{
class GenericList
{
static void Main(string[] args)
{
// The .NET Framework 1.1 way to create a List
ArrayList list = new ArrayList();
// Add an integer to the List
list.Add(3);
// Add a string to the List. This will compile,
// but may cause a Runtime Error!
list.Add("This will trigger a Runtime Error!");
// We cast to int, but this will causes an InvalidCastException
// when encounter a string in the List.
int t = (int)list[1];
t = 0;
foreach (int x in list)
{
t += x;
}
// The .NET Framework 2.0 way to create a List
List<int> list1 = new List<int>();
// No boxing, no casting
list1.Add(3);
list1.Add(1);
// Compile-time error:
list1.Add("This will trigger a Compile-time Error!");
// Get the values from the List, no casting
int t1 = list1[1];
t1 = 0;
foreach (int x in list1)
{
t1 += x;
}
}
}
}
The .NET Framework 1.1 Way
Any reference or value type that is added to an ArrayList is implicitly upcast to Object. If the items are value types, they must be boxed when added to the list, and unboxed when they are retrieved. Both the casting and the boxing and unboxing operations degrade performance; the effect of boxing and unboxing can be quite significant in scenarios where you must iterate over large collections.
The .NET Framework 2.0 Way
For client code, the only added syntax with List<T> compared to ArrayList is the type argument in the declaration and instantiation. In return for this slightly greater coding complexity, you can create a list that is not only safer than ArrayList, but also significantly faster, especially when the list items are value types.
Creating a custom type-safe List
Of course, you can also create custom generic types and methods to provide your own generalized solutions and design patterns that are type-safe and efficient. The following code example shows a simple generic linked-list class for demonstration purposes. (In most cases, it is recommended that you use the List<T> class provided by the .NET Framework class library, rather than create your own.) The type parameter T is used in several places where a concrete type would normally be used to indicate the type of the item stored in the list. It is used in the following way.
using System;
using System.Collections.Generic;
using System.Text;
// --------------------------------------------------------------------
// The following code example shows a simple generic linked-list
// class for demonstration purposes. (In most cases, it is recommended
// that you use the List<T> class provided by the .NET Framework class
// library, rather than create your own.) The type parameter T is
// used in several places where a concrete type would normally be
// used to indicate the type of the item stored in the list.
// --------------------------------------------------------------------
namespace Generics
{
// Type parameter T in angle brackets <>
public class CustomList<T>
{
// Fields
private Node head;
// The nested class is also generic on T
private class Node
{
// Fields
private Node next;
// T as private member data type
private T data;
// T used in non-generic constructor
public Node(T pData)
{
next = null;
data = pData;
}
// Properties
public Node Next
{
get
{
return next;
}
set
{
next = value;
}
}
// T as return type of the Property
public T Data
{
get
{
return data;
}
set
{
data = value;
}
}
}
// Constructor
public CustomList()
{
head = null;
}
// T as method parameter type:
public void Add(T pType)
{
Node n = new Node(pType);
n.Next = head;
head = n;
}
// Enables foreach on the List
public IEnumerator<T> GetEnumerator()
{
Node current = head;
while (current != null)
{
yield return current.Data;
current = current.Next;
}
}
// Provides indexing on the List
public T this[int index]
{
get
{
int ctr = 0;
Node current = head;
while (current != null && ctr <= index)
{
if (ctr == index)
{
return current.Data;
}
else
{
current = current.Next;
}
++ctr;
}
return default(T);
}
}
// Provides ToString() on the List
public override string ToString()
{
if (this.head != null)
{
return this.head.ToString();
}
else
{
return string.Empty;
}
}
}
// Test the CustomList
class TestCustomList
{
// My own Class of any Datatype
private class ExampleClass
{
private object o;
public ExampleClass(object obj)
{
o = obj;
}
public object objGet
{
get
{
return o;
}
}
}
static void Main()
{
// Declare a List of type int, then loop through the List
CustomList<int> list1 = new CustomList<int>();
for (int x = 0; x < 10; x++)
{
list1.Add(x);
}
foreach (int i in list1)
{
System.Console.Write(i + " ");
}
System.Console.WriteLine("\nDone\n");
// Declare a List of type string, then loop through the List
CustomList<string> list2 = new CustomList<string>();
list2.Add("Martin");
list2.Add("Zahn");
list2.Add("Oberdiessbach");
foreach (string s in list2)
{
System.Console.Write(s + " ");
}
System.Console.WriteLine("\nDone\n");
// Use Indexer on the List
string s1 = list2[1];
// Declare a List of type ExampleClass, then loop through the List
// ExampleClass holds any DataType.
CustomList<ExampleClass> list3 = new CustomList<ExampleClass>();
ExampleClass a = new ExampleClass(7);
list3.Add(a);
ExampleClass b = new ExampleClass("Hello");
list3.Add(b);
foreach (ExampleClass o in list3)
{
System.Console.Write(o.objGet.ToString() + " ");
}
System.Console.WriteLine("\nDone");
// Use Indexer on the List
ExampleClass c = list3[0];
System.Console.Write("From Indexer: " + c.objGet.ToString() + "\n");
}
}
}
using System.Collections.Generic;
using System.Text;
// --------------------------------------------------------------------
// The following code example shows a simple generic linked-list
// class for demonstration purposes. (In most cases, it is recommended
// that you use the List<T> class provided by the .NET Framework class
// library, rather than create your own.) The type parameter T is
// used in several places where a concrete type would normally be
// used to indicate the type of the item stored in the list.
// --------------------------------------------------------------------
namespace Generics
{
// Type parameter T in angle brackets <>
public class CustomList<T>
{
// Fields
private Node head;
// The nested class is also generic on T
private class Node
{
// Fields
private Node next;
// T as private member data type
private T data;
// T used in non-generic constructor
public Node(T pData)
{
next = null;
data = pData;
}
// Properties
public Node Next
{
get
{
return next;
}
set
{
next = value;
}
}
// T as return type of the Property
public T Data
{
get
{
return data;
}
set
{
data = value;
}
}
}
// Constructor
public CustomList()
{
head = null;
}
// T as method parameter type:
public void Add(T pType)
{
Node n = new Node(pType);
n.Next = head;
head = n;
}
// Enables foreach on the List
public IEnumerator<T> GetEnumerator()
{
Node current = head;
while (current != null)
{
yield return current.Data;
current = current.Next;
}
}
// Provides indexing on the List
public T this[int index]
{
get
{
int ctr = 0;
Node current = head;
while (current != null && ctr <= index)
{
if (ctr == index)
{
return current.Data;
}
else
{
current = current.Next;
}
++ctr;
}
return default(T);
}
}
// Provides ToString() on the List
public override string ToString()
{
if (this.head != null)
{
return this.head.ToString();
}
else
{
return string.Empty;
}
}
}
// Test the CustomList
class TestCustomList
{
// My own Class of any Datatype
private class ExampleClass
{
private object o;
public ExampleClass(object obj)
{
o = obj;
}
public object objGet
{
get
{
return o;
}
}
}
static void Main()
{
// Declare a List of type int, then loop through the List
CustomList<int> list1 = new CustomList<int>();
for (int x = 0; x < 10; x++)
{
list1.Add(x);
}
foreach (int i in list1)
{
System.Console.Write(i + " ");
}
System.Console.WriteLine("\nDone\n");
// Declare a List of type string, then loop through the List
CustomList<string> list2 = new CustomList<string>();
list2.Add("Martin");
list2.Add("Zahn");
list2.Add("Oberdiessbach");
foreach (string s in list2)
{
System.Console.Write(s + " ");
}
System.Console.WriteLine("\nDone\n");
// Use Indexer on the List
string s1 = list2[1];
// Declare a List of type ExampleClass, then loop through the List
// ExampleClass holds any DataType.
CustomList<ExampleClass> list3 = new CustomList<ExampleClass>();
ExampleClass a = new ExampleClass(7);
list3.Add(a);
ExampleClass b = new ExampleClass("Hello");
list3.Add(b);
foreach (ExampleClass o in list3)
{
System.Console.Write(o.objGet.ToString() + " ");
}
System.Console.WriteLine("\nDone");
// Use Indexer on the List
ExampleClass c = list3[0];
System.Console.Write("From Indexer: " + c.objGet.ToString() + "\n");
}
}
}
Generics Conclusion
o Use generic types to maximize code reuse, type safety, and performance.
o The most common use of generics is to create collection classes.
o The .NET Framework class library contains several new generic collection classes in the System.Collections.Generic namespace. These should be used whenever possible in place of classes such as ArrayList in the System.Collections namespace.
o You can create your own generic interfaces, classes, methods, events and delegates.
o Generic classes may be constrained to enable access to methods on particular data types.
o Information on the types used in a generic data type may be obtained at run-time by means of reflection.
The namespace contains a lot of generic classes, structures, and interfaces like
· Dictionary
· List
· Queue
· SortedDictionary
· Stack
The namespace contains a lot of generic classes, structures, and interfaces like
· Dictionary
· List
· Queue
· SortedDictionary
· Stack
No comments:
Post a Comment