Introduction
This article discusses the details of the generics concept of C# and also will explain in detail how they are implemented, the benefits of the programming model, and unique innovations, such as constraints, generic methods, and generic inheritance. In addition, I have also discussed how the .NET Framework utilizes generics.
What are Generics?
Generics are the most powerful feature of C#.
Generics allow you to define type-safe data structures without committing to actual data types. This results in a significant performance boost and higher quality code, because you get to reuse data processing algorithms without duplicating type-specific code.
Figure 1. Source.Code collections website
Generics are similar to C++ templates but are drastically different in implementation and capabilities. This article discusses the problem space generics address, how they are implemented, the benefits of the programming model, and unique innovations, such as constraints, generic methods and delegates, and generic inheritance. You will also see how generics are utilized in other areas of the .NET Framework, such as reflection, arrays, collections, serialization, and remoting, and how to improve on the basic offering.
Generic Assembly
The generic is a type of collection that is available as the part of Collection namespace assembly.
What is a System CollectionsGeneric?
Generics allow you to delay the specification of the data type of programming elements in a class or a method until it is actually used in the program. In other words, generics allow you to write a class or method that can work with any data type.
You write the specifications for the class or the method, with substitute parameters for data types. When the compiler encounters a constructor for the class or a function call for the method, it generates code to handle the specific data type. A simple example would help in understanding the concept.
Creating Generic List<T> Collections
Generic List collection is similar to arrays. You declare the List, populate its members, and then access the members. Here's a code example of how to use a List.
// Sample Generic List of integers
List<int> myIntegers = new List<int>();
// Sample Generic List of strings
List<string> myStrings = new List<string>();
The above two codes can be replaced by a single line of code using the Generics concept.
List<T> myValue = new List<T>();
Based on the usage, the [T] will behave as either “int” or “string” or any data type based on the executing application usage.
Generics Features
- Generics has the best features that can make your application better by its operations; Generics will maximize code reuse, type safety, and performance.
- You can create generic collection classes. The .NET Framework class library contains several new generic collection classes in the System.Collections.Generic namespace, as discussed earlier.
- You may use these generic collection classes instead of the collection classes in the System. Collections namespace.
- You can create your own generic interfaces, classes, methods, events, and delegates.
- You may create generic classes constrained to enable access to methods on particular data types.
- You may get information on the types used in a generic data type at run-time by means of reflection.
Benefits of using Generics
Generics in .NET let you reuse code and the effort you put into implementing it. The types and internal data can change without causing code bloat, regardless of whether you are using value or reference types. You can develop, test, and deploy your code once, and reuse it with any type, including future types, all with full compiler support and type safety.
The generic code does not force the boxing and unboxing of value types, or the downcasting of reference types; performance is greatly improved.
With value types, there is typically a 200 percent performance gain, and with reference types, you can expect up to a 100 percent performance gain in accessing the type (of course, the application as a whole may or may not experience any performance improvements).
The source code available with this article includes a micro-benchmark application, which executes a stack in a tight loop. The application lets you experiment with value and reference types on an Object-based stack and a generic stack, as well as changing the number of loop iterations to see the effect generics have on performance.
Example
using System;
using System.Collections.Generic;
namespace GenericSampleApp
{
/// <summary>
/// SampleGenericArray
/// </summary>
/// <typeparam name="T"></typeparam>
public class SampleGenericArray<T>
{
private T[] array;
/// <summary>
/// SampleGenericArray - Constructor
/// </summary>
/// <param name="size">size</param>
public SampleGenericArray(int size)
{
array = new T[size + 1];
}
/// <summary>
/// getMyItem - Get Property to return the value
/// </summary>
/// <param name="index">index</param>
/// <returns></returns>
public T getMyItem(int index)
{
return array[index];
}
/// <summary>
/// setMyItem - Set the Property to set value
/// </summary>
/// <param name="index">index</param>
/// <param name="value">value</param>
public void setMyItem(int index, T value)
{
array[index] = value;
}
}
public class SampleTest
{
/// <summary>
/// Main
/// </summary>
/// <param name="args"></param>
public static void Main(string[] args)
{
// Declaring an int array
SampleGenericArray<int> intArray = new SampleGenericArray<int>(5);
// Setting up values
for (int c = 0; c < 5; c++)
{
intArray.setMyItem(c, c * 5);
}
// Retrieving the values
for (int c = 0; c < 5; c++)
{
Console.Write(intArray.getMyItem(c) + " ");
}
Console.WriteLine();
// Declaring a character array
SampleGenericArray<char> charArray = new SampleGenericArray<char>(5);
// Setting values
for (int c = 0; c < 5; c++)
{
charArray.setMyItem(c, (char)(c + 97));
}
// Retrieving the values
for (int c = 0; c < 5; c++)
{
Console.Write(charArray.getMyItem(c) + " ");
}
Console.WriteLine();
Console.ReadKey();
}
}
}
Output