Interfaces
Interfaces define contracts — method signatures without implementation (until default interface methods).
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.
classDiagramclass IRepository~T~ {<<interface>>+GetById(id)+Save(entity)}class EfRepository~T~ {+GetById(id)+Save(entity)}IRepository~T~ <|.. EfRepository~T~
Step-by-step explanation
- Class lists interfaces after base class: class X : Base, IA, IB.
- Compiler verifies all members implemented.
- Interface reference only exposes interface members.
- DI registers Type pairs for interface resolution.
- Mock frameworks implement interfaces in tests dynamically.
- Sealed interface implementations prevent further override of behavior.
Practical code example
Multiple interfaces — auditable entity and repository:
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 IAuditabledefines 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 : classconstrains 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 : IAuditableimplements the audit interface alongside product-specific properties.CreatedAt { get; init; } = DateTimeOffset.UtcNowstamps creation time at initialization.ModifiedAt { get; private set; }can only change through class methods likeTouch().Touch() => ModifiedAt = DateTimeOffset.UtcNowupdates the modification timestamp in one place.
Key takeaway: IAuditable cross-cuts entities. Generic IEntityRepository
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?
Q2BeginnerInterface vs abstract class?
Q3IntermediateExplicit interface implementation why?
Q4IntermediateIEnumerable covariance?
Q5AdvancedEvolve published interface without breaking implementers?
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.