C#

C# Fundamentals

19 lessons

Progress0%
1. Introduction to C#
1What is C#?
2. Variables and Data Types
1Data Types in C#
3. Control Flow
ConditionalsLoops
4. Methods
Defining MethodsOptional Parameters and Overloading
5. Object-Oriented Programming
Classes and PropertiesInheritanceInterfaces and Generics
6. LINQ and Async
LINQ Queriesasync/await
7. Exception Handling
try/catch/finally & Exception TypesCustom Exceptions & IDisposable
8. Delegates & Events
Delegates & LambdaEvents & Event Handlers
9. Records & Pattern Matching
Record TypesPattern Matching & Switch Expressions
10. File I/O & JSON
File & Stream OperationsJSON Serialization
All Tutorials
C#Object-Oriented Programming
Lesson 9 of 19 min
Chapter 5 · Lesson 3

Interfaces and Generics

Interfaces and Generics in C#

Interfaces An interface defines a contract. Any class or struct can implement it:

csharp
interface IDrawable { void Draw(); }
class Circle : IDrawable { public void Draw() { … } }

C# 8+ allows default interface methods.

Generic types Generics parameterise types and methods for reuse without sacrificing type safety:

csharp
class Stack<T> { … }
var s = new Stack<int>();

where T : constraints Restrict what types can be used as the type parameter:

  • where T : class — reference type
  • where T : struct — value type
  • where T : new() — has a parameterless constructor
  • where T : IComparable<T> — implements an interface

List<T> and Dictionary<K,V> Generic collections from System.Collections.Generic provide type-safe data structures without boxing overhead.

Key points:

  • A class can implement multiple interfaces.
  • Prefer List<T> over ArrayList (which uses object and requires casting).
  • Use Dictionary<K,V> for O(1) key-based lookups.

Code Examples

Interface and generic methodcsharp
using System;
using System.Collections.Generic;

interface IRepository<T> {
    void Add(T item);
    IEnumerable<T> GetAll();
}

class InMemoryRepository<T> : IRepository<T> {
    private readonly List<T> _items = new();
    public void Add(T item) => _items.Add(item);
    public IEnumerable<T> GetAll() => _items;
}

class Program {
    static void Main() {
        var repo = new InMemoryRepository<string>();
        repo.Add("Alice");
        repo.Add("Bob");
        repo.Add("Charlie");
        foreach (var name in repo.GetAll())
            Console.WriteLine(name);
    }
}

A generic repository can hold any type. The interface contract ensures consistent behaviour regardless of T.

Dictionary<K,V> and List<T>csharp
using System;
using System.Collections.Generic;

class Program {
    static void Main() {
        var scores = new Dictionary<string, int> {
            ["Alice"] = 95,
            ["Bob"] = 80
        };
        scores["Charlie"] = 90;

        foreach (var (name, score) in scores)
            Console.WriteLine($"{name}: {score}");

        Console.WriteLine($"Has Dave: {scores.ContainsKey("Dave")}");
        Console.WriteLine($"Alice: {scores.GetValueOrDefault("Alice")}");

        var evens = new List<int>();
        for (int i = 2; i <= 10; i += 2) evens.Add(i);
        Console.WriteLine(string.Join(", ", evens));
    }
}

Dictionary<K,V> provides O(1) lookups. Tuple deconstruction in foreach makes key-value iteration concise.

Quick Quiz

1. What does `where T : new()` constraint require?

2. Why prefer `List<T>` over `ArrayList`?

Was this lesson helpful?

PreviousNext