Last active
April 27, 2024 10:26
-
-
Save 5cover/8d2852608422a8a5ff51f19c51f2dc9c to your computer and use it in GitHub Desktop.
C# Visitor pattern compile-time safety example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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