Understanding Generics in C#: A Comprehensive Guide
Written on
Chapter 1: Introduction to Generics
Generics in C# serve as a powerful tool for defining classes, interfaces, methods, and delegates that utilize placeholder types. These types, referred to as type parameters, can be substituted with specific types during the creation of generic instances or the invocation of generic methods. By employing generics, developers can craft more adaptable and reusable code, enabling the design of components compatible with any data type.
The FruitList Class:
- It contains a private array _fruitItems of type T[] to hold elements.
- It maintains a private integer _count to track the number of elements in the list.
- The constructor initializes the _fruitItems array with a defined capacity and sets _count to 0.
- The Add method allows for adding elements, taking an item of type T as a parameter and placing it in the next available index of the _fruitItems array while incrementing _count.
- The Get method retrieves an element from the list at a specified index, returning the element stored at that index in the _fruitItems array.
The Main Method:
- An instance of FruitList named priceList is created to store integers, initialized with a capacity of 5.
- Two integers, 10 and 20, are added to priceList.
- The first element of priceList, which is 10, is printed.
- Another instance, nameList, is created to hold strings with a capacity of 3.
- Two strings, "Apple" and "Mango", are added to nameList.
- An attempt to add nameList directly to a new instance of FruitList named fruitNameList fails, as the Add method expects a single item of type T.
- A list called pricesList containing integers 12, 16, 8, and 22 is created.
- An attempt to add pricesList to fruitPriceList fails for the same reason as above.
- The fourth element of fruitPriceList, which should be 8, is printed.
using System;
public class FruitList<T>
{
private T[] _fruitItems;
private int _count;
public FruitList(int capacity)
{
_fruitItems = new T[capacity];
_count = 0;
}
public void Add(T item)
{
_fruitItems[_count] = item;
_count++;
}
public T Get(int index)
{
return _fruitItems[index];}
}
class CSharpProgram
{
public static void Main(string[] args)
{
FruitList<int> priceList = new FruitList<int>(5);
priceList.Add(10);
priceList.Add(20);
Console.WriteLine(priceList.Get(0));
FruitList<string> nameList = new FruitList<string>(3);
nameList.Add("Apple");
nameList.Add("Mango");
Console.WriteLine(nameList.Get(1));
}
}
Chapter 2: Generics Collections in C#
The FruitList class is designed generically; however, it must be properly declared to function correctly. The structure includes:
- A private array _fruitItems of type T[].
- A private integer _count for tracking elements.
- A constructor that initializes the _fruitItems array and sets _count to 0.
- An Add method that now accepts a list, iterating over its items to add each to _fruitItems.
- A Get method to retrieve elements based on an index.
The Main Method:
- A list named namesList holds strings "Cherry", "Orange", and "Banana".
- An instance of FruitList named fruitNameList is created with a capacity of 3.
- The updated Add method allows adding namesList to fruitNameList.
- The second element of fruitNameList, expected to be "Orange", is printed.
- A list named pricesList containing integers is created.
- An instance of FruitList named fruitPriceList is created with a capacity of 4.
- The updated Add method allows adding pricesList to fruitPriceList.
- The fourth element of fruitPriceList, expected to be 8, is printed.
using System;
using System.Collections.Generic;
public class FruitList<T>
{
private T[] _fruitItems;
private int _count;
public FruitList(int capacity)
{
_fruitItems = new T[capacity];
_count = 0;
}
public void Add(List<T> items)
{
foreach (T item in items)
{
_fruitItems[_count] = item;
_count++;
}
}
public T Get(int index)
{
return _fruitItems[index];}
}
class CSharpProgram
{
public static void Main(string[] args)
{
List<string> namesList = new List<string> { "Cherry", "Orange", "Banana" };
FruitList<string> fruitNameList = new FruitList<string>(3);
fruitNameList.Add(namesList);
Console.WriteLine(fruitNameList.Get(1));
List<int> pricesList = new List<int> { 12, 16, 8, 22 };
FruitList<int> fruitPriceList = new FruitList<int>(4);
fruitPriceList.Add(pricesList);
Console.WriteLine(fruitPriceList.Get(3));
}
}
In this video, we explore why you might not need Generics in C#. Watch as we delve into practical scenarios and examples that illustrate this concept.
Chapter 3: Generic Methods in C#
The GenericMethodClass showcases a generic method called Swap, which accepts two parameters of type T by reference. This method effectively swaps the values of the two parameters.
The Main Method:
- Two integer variables, x and y, are initialized.
- Their values are printed before the swap.
- The Swap method is invoked, with x and y as arguments.
- The values of x and y are printed after the swap, confirming successful value interchange.
- Two string variables, str1 and str2, are initialized.
- Their values are printed before the swap.
- The Swap method is called again, swapping the string values.
- Finally, the swapped values of str1 and str2 are printed.
using System;
public class GenericMethodClass<T>
{
public void Swap(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
}
class CSharpProgram
{
public static void Main(string[] args)
{
int x = 5;
int y = 10;
Console.WriteLine($"Before swapping: x = {x}, y = {y}");
GenericMethodClass<int> genericNumList = new GenericMethodClass<int>();
genericNumList.Swap(ref x, ref y);
Console.WriteLine($"After swapping: x = {x}, y = {y}");
string str1 = "Hello";
string str2 = "World";
Console.WriteLine($"Before swapping: str1 = {str1}, str2 = {str2}");
GenericMethodClass<string> genericStringList = new GenericMethodClass<string>();
genericStringList.Swap(ref str1, ref str2);
Console.WriteLine($"After swapping: str1 = {str1}, str2 = {str2}");
}
}
Chapter 4: Understanding Generics Constraints in C#
The SimpleClass demonstrates how to define a generic class with constraints. It requires that the type argument T implements the IComparable interface, enabling comparison operations.
The Main Method:
- An instance of SimpleClass is created with int as the type argument.
- Items are added to the array and the maximum value is found and printed.
- Another instance is created with string as the type argument.
- Strings are added, and the maximum string value is found and printed.
using System;
public class SimpleClass<T> where T : IComparable
{
private T[] arrayFruit;
public SimpleClass(int size)
{
arrayFruit = new T[size];}
public T FindMax()
{
T max = arrayFruit[0];
foreach (T item in arrayFruit)
{
if (item.CompareTo(max) > 0)
{
max = item;}
}
return max;
}
public void AddItem(int index, T item)
{
arrayFruit[index] = item;}
}
class CSharpProgram
{
public static void Main(string[] args)
{
SimpleClass<int> numArray = new SimpleClass<int>(3);
numArray.AddItem(0, 5);
numArray.AddItem(1, 10);
numArray.AddItem(2, 3);
int maxNum = numArray.FindMax();
Console.WriteLine("Maximum value in int array: " + maxNum);
SimpleClass<string> stringArray = new SimpleClass<string>(3);
stringArray.AddItem(0, "apple");
stringArray.AddItem(1, "banana");
stringArray.AddItem(2, "orange");
string maxString = stringArray.FindMax();
Console.WriteLine("Maximum value in string array: " + maxString);
}
}
Conclusion
In summary, generics in C# offer a robust framework for building flexible and reusable components. By understanding and implementing generics, developers can enhance the versatility of their code, leading to better software design and implementation.
Thank you for reading! For more insightful tutorials, please visit C-Sharp Tutorial. If you found this article beneficial, feel free to show your support by clapping and following the author using the button below.