Дженерики - это когда вы предполагаете использование определенных типов в контексте и вам нужно связать их по определенному критерию. Кроме того, это избавляет вас от необходимости приводить типы в дальнейшей реализации кода.
Как пример, задача: Вам нужно реализовать класс A со свойством F, которое может быть любого типа, реализованного от интерфейса IB:
Решение 1. Неоптимизированный подход без дженериков:
class A {
IB F { get; }
}
A a = new A();
C c = a.F as C; // Здесь придется приводить к типу.
Решение 2. Вариант с дженериками:
class A<T> {
T F { get; }
}
A<C> a = new A<C>();
C c = a.F; // Здесь лежит сразу экземпляр типа C, приводить к типу не нужно.