Skip to content

Instantly share code, notes, and snippets.

@5cover
Last active April 27, 2024 10:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 5cover/8d2852608422a8a5ff51f19c51f2dc9c to your computer and use it in GitHub Desktop.
Save 5cover/8d2852608422a8a5ff51f19c51f2dc9c to your computer and use it in GitHub Desktop.
C# Visitor pattern compile-time safety example
// This visitor implementation attemps to make the best of both worlds, between C#'s functional features and compile-time safety.
// Using extension methods as visitors makes it a runtime error for there not to be an equivalent visitor for every subclass of the element hierarchy.
// This approach ensures compile-time safety, while minimizing boilerplate. It's a step closer to the ideal of "if it compiles, it works".
using System;
Room room = new Bathroom(10);
RoomVisitor visitorPrintSurface = new(
bathroom => Console.WriteLine($"Surface of bathroom: {bathroom.Surface}"),
bedroom => Console.WriteLine($"Surface of bedroom: {bedroom.Surface}"),
kitchen => Console.WriteLine($"Surface of kitchen: {kitchen.Surface}"),
hall => Console.WriteLine($"Surface of kitchen: {hall.Surface}"));
room.Accept(visitorPrintSurface);
sealed class RoomVisitor(
Action<Bathroom> bathroom,
Action<Bedroom> bedroom,
Action<Kitchen> kitchen,
Action<Hall> hall)
{
public void Visit(Bathroom room) => bathroom(room);
public void Visit(Bedroom room) => bedroom(room);
public void Visit(Kitchen room) => kitchen(room);
public void Visit(Hall room) => hall(room);
}
abstract record Room(double Surface)
{
public abstract void Accept(RoomVisitor v);
}
sealed record Bathroom(double Surface) : Room(Surface)
{
public override void Accept(RoomVisitor v) => v.Visit(this);
}
sealed record Bedroom(double Surface) : Room(Surface)
{
public override void Accept(RoomVisitor v) => v.Visit(this);
}
sealed record Kitchen(double Surface) : Room(Surface)
{
public override void Accept(RoomVisitor v) => v.Visit(this);
}
sealed record Hall(double Surface) : Room(Surface)
{
public override void Accept(RoomVisitor v) => v.Visit(this);
}
using System;
Room room = new Bedroom(10);
RoomVisitor visitorPrintSurface = new RoomVisitorPrintSurface();
room.Accept(visitorPrintSurface);
sealed class RoomVisitorPrintSurface : RoomVisitor
{
public void Visit(Bathroom bathroom) => Console.WriteLine($"Surface of bathroom: {bathroom.Surface}");
public void Visit(Bedroom bedroom) => Console.WriteLine($"Surface of bedroom: {bedroom.Surface}");
public void Visit(Kitchen kitchen) => Console.WriteLine($"Surface of kitchen: {kitchen.Surface}");
}
interface RoomVisitor
{
void Visit(Bathroom bathroom);
void Visit(Bedroom bedroom);
void Visit(Kitchen kitchen);
}
abstract record Room(double Surface)
{
public abstract void Accept(RoomVisitor visitor);
}
sealed record Bathroom(double Surface) : Room(Surface)
{
public override void Accept(RoomVisitor visitor) => visitor.Visit(this);
}
sealed record Bedroom(double Surface) : Room(Surface)
{
public override void Accept(RoomVisitor visitor) => visitor.Visit(this);
}
sealed record Kitchen(double Surface) : Room(Surface)
{
public override void Accept(RoomVisitor visitor) => visitor.Visit(this);
}
sealed record Hall(double Surface) : Room(Surface)
{
public override void Accept(RoomVisitor visitor) => visitor.Visit(this);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment