C# Programming Tutorial 0/45 lessons ~6 min read Lesson 20

    Interfaces

    Interfaces define contracts — method signatures without implementation (until default interface methods).

    Course progress0%
    Focus
    10 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Interfaces define contracts — method signatures without implementation (until default interface methods). A class implements multiple interfaces: IDisposable, IComparable, custom domain ports. ASP.NET Core action methods, EF configurations, and middleware all implement interfaces.

    C# 8+ default interface methods allow evolving interfaces without breaking implementers — used sparingly. Interviewers ask interface vs abstract class, explicit implementation, and generic variance.

    Interfaces are the primary extension point in testable .NET architecture.

    The story

    A warehouse inventory system tracks products that need audit metadata — when each record was created and last modified. The same IAuditable interface applies to products, shipments, and purchase orders, while a generic IEntityRepository<T> provides consistent CRUD operations for any entity type.

    Understanding the topic

    Key concepts

    • interface declares contract; class/struct implements.
    • Multiple interface implementation allowed.
    • Explicit implementation: InterfaceName.Method() hides from public API.
    • Generic interfaces: IRepository, IComparable.
    • Covariance out T; contravariance in T on delegates/interfaces.
    • Default interface members (D# 8) optional implementation.
    text
    classDiagram
    class IRepository~T~ {
    <<interface>>
    +GetById(id)
    +Save(entity)
    }
    class EfRepository~T~ {
    +GetById(id)
    +Save(entity)
    }
    IRepository~T~ <|.. EfRepository~T~

    Step-by-step explanation

    1. Class lists interfaces after base class: class X : Base, IA, IB.
    2. Compiler verifies all members implemented.
    3. Interface reference only exposes interface members.
    4. DI registers Type pairs for interface resolution.
    5. Mock frameworks implement interfaces in tests dynamically.
    6. Sealed interface implementations prevent further override of behavior.

    Practical code example

    Multiple interfaces — auditable entity and repository:

    csharp
    namespace TechLearningPro.Interfaces;
    public interface IAuditable
    {
    DateTimeOffset CreatedAt { get; }
    DateTimeOffset? ModifiedAt { get; }
    }
    public interface IEntityRepository<T> where T : class
    {
    Task<T?> GetAsync(Guid id, CancellationToken ct);
    Task InsertAsync(T entity, CancellationToken ct);
    }
    public sealed class Product : IAuditable
    {
    public Guid Id { get; init; }
    public string Name { get; init; } = "";
    public DateTimeOffset CreatedAt { get; init; } = DateTimeOffset.UtcNow;
    public DateTimeOffset? ModifiedAt { get; private set; }
    public void Touch() => ModifiedAt = DateTimeOffset.UtcNow;
    }

    Line-by-line code explanation

    • interface IAuditable defines audit fields every tracked entity must expose.
    • DateTimeOffset CreatedAt { get; } records when the entity was first saved.
    • DateTimeOffset? ModifiedAt { get; } is nullable until the first update occurs.
    • interface IEntityRepository<T> where T : class constrains T to reference types only.
    • Task<T?> GetAsync(Guid id, ...) retrieves a single entity by primary key.
    • Task InsertAsync(T entity, ...) adds a new entity through the repository contract.
    • class Product : IAuditable implements the audit interface alongside product-specific properties.
    • CreatedAt { get; init; } = DateTimeOffset.UtcNow stamps creation time at initialization.
    • ModifiedAt { get; private set; } can only change through class methods like Touch().
    • Touch() => ModifiedAt = DateTimeOffset.UtcNow updates the modification timestamp in one place.

    Key takeaway: IAuditable cross-cuts entities. Generic IEntityRepository constrains reference types. Small focused interfaces follow ISP.

    Real-world use

    Where you'll use this in production

    • ASP.NET Core middleware implementing IMiddleware.
    • IDisposable for resource cleanup using patterns.
    • Plugin host loading IPlugin from assemblies.
    • Test mocks of IEmailSender in unit tests.

    Best practices

    • One interface per role — segregate large contracts.
    • Suffix domain interfaces meaningfully — IOrderService not IOrderManager2.
    • Implement IDisposable correctly when holding unmanaged resources.
    • Prefer interface injection in constructors.
    • Avoid marker interfaces without behavior — use attributes if needed.

    Common mistakes

    • Fat interface forcing empty NotImplementedException stubs.
    • Public class exposing explicit implementation unnecessarily.
    • Implementing IDisposable without suppress finalize pattern when needed.
    • Changing interface breaking all implementers — use extension or new interface.

    Advanced interview questions

    Q1BeginnerCan struct implement interface?
    Yes — IList, IComparable common on readonly struct.
    Q2BeginnerInterface vs abstract class?
    Interface: multiple, no state (traditionally); abstract: single, shared code.
    Q3IntermediateExplicit interface implementation why?
    Resolve name collision; hide member from public class API.
    Q4IntermediateIEnumerable covariance?
    IEnumerable allows IEnumerable as IEnumerable if Dog : Animal.
    Q5AdvancedEvolve published interface without breaking implementers?
    Default interface method with default body; or new ICustomerServiceV2; adapter pattern for migration.

    Summary

    Interfaces define contracts; classes implement multiple. Generic and auditable interfaces cross-cut enterprise domains. DI and testing rely heavily on interface abstractions. Keep interfaces small and role-specific. Next: arrays — first collection type.

    Ready to mark this lesson complete?Track your journey across the entire course.