Generics (Classes & Methods)
Part of the Object Oriented Programming section of Coddy's C# journey — lesson 42 of 70.
Generics allow you to write classes and methods that work with any data type while maintaining type safety. Instead of writing separate classes for IntList, StringList, and DoubleList, you write one generic class that handles them all.
A generic class uses a type parameter, typically named T, enclosed in angle brackets:
public class Box<T>
{
private T content;
public void Store(T item) => content = item;
public T Retrieve() => content;
}
// Usage with different types
Box<int> intBox = new Box<int>();
intBox.Store(42);
Box<string> stringBox = new Box<string>();
stringBox.Store("Hello");When you create Box<int>, the compiler replaces every T with int. This provides compile-time type checking - you can't accidentally store a string in an int box.
Generic methods work similarly, allowing a single method to operate on different types:
public static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
int x = 1, y = 2;
Swap(ref x, ref y); // x=2, y=1The compiler infers the type from the arguments, so you don't need to write Swap<int> explicitly. Generics are the foundation of collections like List<T> and Dictionary<TKey, TValue>, enabling reusable, type-safe code.
Challenge
EasyLet's build a generic Storage<T> system that can hold and manage items of any type. You'll create a reusable container class that demonstrates the power of generics - write once, use with integers, strings, or any other type!
You'll organize your code across two files:
Storage.cs: Create a genericStorage<T>class in theDataStoragenamespace. Your storage container should:- Have a private array of type
Tto hold items and aCountproperty tracking how many items are stored - Accept a capacity in its constructor to initialize the internal array
- Have an
Add(T item)method that adds an item if there's room - Have a
Get(int index)method that returns the item at the specified index - Have a
GetAll()method that returns all stored items joined by", "
- Have a private array of type
Program.cs: In your main file, demonstrate your generic class working with two different types. Create aStorage<int>and aStorage<string>, add items to each, and show that the same class works seamlessly with both types.
You will receive four inputs:
- First integer to store
- Second integer to store
- First string to store
- Second string to store
Create an integer storage with capacity 5 and a string storage with capacity 5. Add the two integers to the integer storage and the two strings to the string storage.
Print the output in this format:
Int storage: {GetAll()}
Int count: {Count}
First int: {Get(0)}
String storage: {GetAll()}
String count: {Count}
First string: {Get(0)}For example, if the inputs are 42, 100, Hello, and World, the output should be:
Int storage: 42, 100
Int count: 2
First int: 42
String storage: Hello, World
String count: 2
First string: HelloNotice how you write the Storage<T> class once, but it works perfectly with both int and string - that's the magic of generics! The compiler ensures type safety, so you can't accidentally add a string to your integer storage.
Cheat sheet
Generics allow you to write classes and methods that work with any data type while maintaining type safety.
A generic class uses a type parameter (typically T) enclosed in angle brackets:
public class Box<T>
{
private T content;
public void Store(T item) => content = item;
public T Retrieve() => content;
}When creating an instance, specify the type to replace T:
Box<int> intBox = new Box<int>();
intBox.Store(42);
Box<string> stringBox = new Box<string>();
stringBox.Store("Hello");Generic methods allow a single method to operate on different types:
public static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
int x = 1, y = 2;
Swap(ref x, ref y); // Compiler infers type from argumentsGenerics provide compile-time type checking and are the foundation of collections like List<T> and Dictionary<TKey, TValue>.
Try it yourself
using System;
using DataStorage;
class Program
{
public static void Main(string[] args)
{
// Read inputs
int num1 = Convert.ToInt32(Console.ReadLine());
int num2 = Convert.ToInt32(Console.ReadLine());
string str1 = Console.ReadLine();
string str2 = Console.ReadLine();
// TODO: Create a Storage<int> with capacity 5
// TODO: Add the two integers to the integer storage
// TODO: Create a Storage<string> with capacity 5
// TODO: Add the two strings to the string storage
// TODO: Print the output in the required format
// Int storage: {GetAll()}
// Int count: {Count}
// First int: {Get(0)}
// String storage: {GetAll()}
// String count: {Count}
// First string: {Get(0)}
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesNamespaces & DirectivesIntro to Classes & ObjectsThe 'this' KeywordMethods and ParametersFields vs PropertiesConstructorsObject InitializersRecap - Simple Calculator4Inheritance
Basic Inheritance (:) SyntaxThe 'base' KeywordVirtual & Override KeywordsSealed ClassesThe 'object' Base ClassRecap - Employee Hierarchy7Advanced Features
Operator OverloadingIndexers (this[])ToString() OverrideExtension MethodsRecap - Custom List2Properties & Static Members
Auto-Implemented PropertiesRead/Write-Only PropertiesStatic Fields & MethodsStatic ClassesExpression-Bodied Members5Polymorphism & Interfaces
Compile vs Runtime PolyInterface vs Abstract ClassMultiple InterfacesExplicit InterfacesUpcasting & DowncastingRecap - Shape Calculator8Advanced OOP Concepts
Composition over InheritanceGenerics (Classes & Methods)Delegates and EventsAttributes and ReflectionIDisposable & using StatementDependency Injection Basics