7.0 What This Teaches
- Interface syntax: declaring members without implementation
- Implementing one or more interfaces on a class
- Programming to an interface (polymorphism through interface references)
- Common built-in interfaces:
IComparable<T>,IEnumerable<T>,IDisposable - Default interface methods (C# 8+)
- Explicit interface implementation
7.1 Interface Syntax
interface IShape
{
double Area();
double Perimeter();
string Describe() => $"area={Area():F2}"; // default method (C# 8+)
}
I. Members declared in an
interface are implicitly public and abstract.
7.2 Implementing Interfaces
class Circle : IShape
{
public double Radius { get; }
public Circle(double r) { Radius = r; }
public double Area() => Math.PI * Radius * Radius;
public double Perimeter() => 2 * Math.PI * Radius;
}
class Square : IShape
{
public double Side { get; }
public Square(double s) { Side = s; }
public double Area() => Side * Side;
public double Perimeter() => 4 * Side;
}
IShape c = new Circle(3);
IShape s = new Square(4);
Console.WriteLine(c.Describe()); // area=28.27
Console.WriteLine(s.Describe()); // area=16.00
7.3 Multiple Interfaces
interface IDrawable
{
void Draw(string canvas);
}
interface IResizable
{
void Resize(double factor);
}
class Widget : IDrawable, IResizable
{
public double Width { get; private set; }
public double Height { get; private set; }
public Widget(double w, double h) { Width = w; Height = h; }
public void Draw(string canvas) =>
Console.WriteLine($"Drawing {Width}x{Height} on {canvas}");
public void Resize(double factor)
{
Width *= factor;
Height *= factor;
}
}
7.4 Interface References
static void PrintArea(IShape shape) =>
Console.WriteLine($"{shape.GetType().Name}: area={shape.Area():F2}");
List<IShape> shapes = new() { new Circle(3), new Square(4), new Circle(1) };
foreach (var s in shapes)
PrintArea(s);
is operator tests whether an object implements an interface at
runtime: if (obj is IDisposable d) d.Dispose();
7.5 Common Built-in Interfaces
class Temperature : IComparable<Temperature>
{
public double Celsius { get; }
public Temperature(double c) { Celsius = c; }
public int CompareTo(Temperature? other)
{
if (other is null) return 1;
return Celsius.CompareTo(other.Celsius);
}
public override string ToString() => $"{Celsius}°C";
}
var temps = new[] { new Temperature(100), new Temperature(0), new Temperature(37) };
Array.Sort(temps);
foreach (var t in temps) Console.Write($"{t} ");
0°C 37°C 100°C
IEnumerable<T> (enables
foreach), IDisposable (enables using
statements), IEquatable<T> (typed equality).
7.6 Explicit Interface Implementation
interface IA { void Do(); }
interface IB { void Do(); }
class Dual : IA, IB
{
void IA.Do() => Console.WriteLine("IA.Do");
void IB.Do() => Console.WriteLine("IB.Do");
}
Dual d = new Dual();
((IA)d).Do(); // IA.Do
((IB)d).Do(); // IB.Do
7.7 Example - All Together
// Interfaces - pluggable serialization via interface.
using System.Text.Json;
ISerializer jsonSerializer = new JsonSerializer();
ISerializer csvSerializer = new CsvSerializer();
var data = new[] { ("Alice", 30), ("Bob", 25) };
Console.WriteLine(jsonSerializer.Serialize(data));
Console.WriteLine(csvSerializer.Serialize(data));
interface ISerializer
{
string Serialize(IEnumerable<(string Name, int Age)> rows);
}
class JsonSerializer : ISerializer
{
public string Serialize(IEnumerable<(string Name, int Age)> rows)
{
var items = rows.Select(r => new { r.Name, r.Age });
return System.Text.Json.JsonSerializer.Serialize(items);
}
}
class CsvSerializer : ISerializer
{
public string Serialize(IEnumerable<(string Name, int Age)> rows)
{
var lines = rows.Select(r => $"{r.Name},{r.Age}");
return "Name,Age\n" + string.Join("\n", lines);
}
}
7.8 Exercise
Exercise
- Define an
ILoggerinterface withvoid Log(string message)andstring Name { get; }. - Implement it in
ConsoleLogger(prints to stdout) andListLogger(stores messages in aList<string>and exposes them). - Write a method that accepts
ILoggerand logs three messages through it. Call it with both implementations.
7.9 Common Mistakes
Implementing the wrong method signature
Casting to interface when is/as is safer
IDisposable d = (IDisposable)obj; // throws InvalidCastException if obj doesn't implement it
if (obj is IDisposable id) id.Dispose(); // safe pattern matching - no exception
Putting too much in an interface
7.10 Key Terms
| Term | Meaning |
|---|---|
| interface | Contract declaring members a class must implement; no fields, no state |
| implement | Provide a concrete body for each member declared by an interface |
| IComparable<T> | Interface enabling sorting; requires CompareTo method |
| IEnumerable<T> | Interface enabling foreach; requires GetEnumerator |
| IDisposable | Interface enabling using statements; requires Dispose method |
| default method | Interface member with a body (C# 8+); used for optional or shared behavior |
| explicit implementation | Qualifying a member with the interface name to resolve ambiguity |
| is / as | Safe runtime interface checks; is tests, as casts (returns null on failure) |