Generics

Write once, use with any type — the foundation of type-safe reusable code in C#


1. What are Generics?

Generics allow you to write reusable, type-safe code that works with different data types without losing compile-time type checking.

public class Box<T>
{
    public T Value { get; set; }
}

Here, T is a placeholder for the actual type.

Box<T> ↓ T = int → Box<int> T = string → Box<string>

Why Use Generics?


2. Generic Classes

var intBox = new Box<int>();
intBox.Value = 10;

var stringBox = new Box<string>();
stringBox.Value = "Hello";
⭐ Key Concept: T is not a specific type. It is a type parameter that represents the actual type supplied by the caller.

3. Multiple Type Parameters

public class Pair<T1, T2>
{
    public T1 First { get; set; }
    public T2 Second { get; set; }
}

var pair = new Pair<int, string>
{
    First = 101,
    Second = "Jithin"
};

4. Generic Methods

public static void Print<T>(T value)
{
    Console.WriteLine(value);
}

// Usage
Print(100);            // T = int
Print("Hello");      // T = string
Print(DateTime.Now);   // T = DateTime

5. Generic Type Inference

Helper.Print<int>(100);  // Explicit
Helper.Print(100);      // Inferred — compiler determines T = int

6. Generic Constraints

Constraints do two important things:

  1. Restrict which types can be used as T
  2. Give the compiler guarantees about what T can do

7. class Constraint

public void Process<T>(T value)
    where T : class
{
}

Process("Hello");   // ✅ string is reference type
Process(10);          // ❌ int is value type

8. struct Constraint

public void Process<T>(T value)
    where T : struct
{
}

Process(10);          // ✅ int is value type
Process("Hello");   // ❌ string is reference type

9. Interface Constraint

public void Save<T>(T entity)
    where T : IEntity
{
    Console.WriteLine(entity.Id);  // Safe — compiler knows T has Id
}
⭐ Key Concept: The constraint gives the compiler a guarantee. where T : IEntity means "Whatever T is, it will implement IEntity."

10. new() Constraint

public T Create<T>()
    where T : new()
{
    return new T();
}

var customer = Create<Customer>();
Important Rule: When combined with other constraints, new() must appear last.

11. Multiple Constraints

public void Process<T>()
    where T : class, IEntity, new()
{
    T entity = new T();
    Console.WriteLine(entity.Id);
}

The declaration where T : class, IEntity, new() means T must satisfy all three requirements simultaneously:


12. Constraint Ordering

1. class / struct 2. Base class 3. Interface 4. new()

13. Why Do Constraints Matter?

Without a constraint:

public void Save<T>(T entity)
{
    Console.WriteLine(entity.Id);  // ❌ Compiler doesn't know if T has Id
}

With a constraint:

public void Save<T>(T entity)
    where T : IEntity
{
    Console.WriteLine(entity.Id);  // ✅ Compiler knows T implements IEntity
}
⭐ Key Principle: A generic constraint gives the compiler enough information to safely use the members guaranteed by that constraint.

14. Common Built-in Generic Types

TypeExample
List<T>List<int> numbers = new();
Dictionary<TKey, TValue>Dictionary<int, string> users = new();
IEnumerable<T>IEnumerable<Customer> customers = ...;
Nullable<T> / T?int? age = null;

15. Generics vs. object

FeatureobjectGenerics
Compile-time type safetyLimited✅ Strong
Explicit castingOften requiredUsually not required
Runtime type errorsMore possibleReduced
Type clearly expressed❌✅

16. Generic Repository Example

public class Repository<T>
    where T : class, IEntity, new()
{
    public T Create()
    {
        return new T();
    }
}

// Usage
var repository = new Repository<Customer>();
Customer customer = repository.Create();

17. Interview Takeaways ⭐

Generics

Generics → Reusable, strongly typed code.

T

T → Type parameter, not a specific type.

Generic Method

A method can have its own type parameter: Print<T>(T value)

where T : class

T must be a reference type.

where T : struct

T must be a non-nullable value type.

where T : IEntity

T must implement IEntity.

where T : new()

T must have a public parameterless constructor.


18. Quick Revision

ConceptRemember
GenericsReusable, type-safe code
TType parameter
Generic ClassClass with type parameters
Generic MethodMethod with its own type parameter
Type InferenceCompiler determines T from arguments
where T : classReference type
where T : structNon-nullable value type
where T : IEntityMust implement interface
where T : new()Public parameterless constructor
new() orderingMust be last

19. One-Line Interview Answers ⭐

Generics allow us to write reusable, type-safe code that works with different data types while maintaining compile-time type checking.
T is a type parameter that represents the actual type supplied when a generic class or method is used.
A generic constraint restricts the types that can be used as a type parameter and gives the compiler guarantees about the capabilities of that type.
Generics provide compile-time type safety, reduce explicit casting, and can avoid boxing and unboxing for value types.