TGViewer
Библиотека шарписта | C#, F#, .NET, ASP.NET Библиотека шарписта | C#, F#, .NET, ASP.NET @csharpproglib · 21.7K subscribers
Post #7086 2.88K
📎 Паттерн Visitor. Почему в C# он больше не нужен

Visitor позволяет добавлять операции к иерархии объектов без изменения их классов. Классический пример — обход AST, дерева документа или иерархии фигур. Вы создаёте отдельный объект-посетитель, который «обходит» структуру и выполняет нужное действие для каждого типа.

Долгое время это был единственный нормальный способ реализовать double dispatch в C#. Но начиная с C# 8 появился switch по типам, а в C# 12 эта возможность стала ещё мощнее. И теперь Visitor выглядит как лишняя обвязка.

Как выглядел Visitor

Допустим, есть иерархия фигур и нужно посчитать площадь.

public interface IShape
{
void Accept(IShapeVisitor visitor);
}

public class Circle : IShape
{
public double Radius { get; init; }
public void Accept(IShapeVisitor visitor) => visitor.Visit(this);
}

public class Rectangle : IShape
{
public double Width { get; init; }
public double Height { get; init; }
public void Accept(IShapeVisitor visitor) => visitor.Visit(this);
}

public interface IShapeVisitor
{
void Visit(Circle circle);
void Visit(Rectangle rectangle);
}

public class AreaCalculator : IShapeVisitor
{
public double Result { get; private set; }

public void Visit(Circle circle) =>
Result = Math.PI * circle.Radius * circle.Radius;

public void Visit(Rectangle rectangle) =>
Result = rectangle.Width * rectangle.Height;
}


Два интерфейса, метод Accept в каждом классе, отдельный класс на каждую операцию. Для двух фигур это терпимо. Для десяти — уже много шаблонного кода.

Как это делается через pattern matching

Тот же расчёт площади в одном switch выражении.

public static double CalculateArea(IShape shape) => shape switch
{
Circle c => Math.PI * c.Radius * c.Radius,
Rectangle r => r.Width * r.Height,
_ => throw new ArgumentException($"Unknown shape: {shape.GetType().Name}")
};


Нет интерфейса IShapeVisitor. Нет метода Accept. Нет отдельного класса калькулятора. Логика читается сверху вниз, в одном месте.

Что происходит, когда добавляется новый тип

Это ключевое отличие. Допустим, вы добавили Triangle.

В Visitor нужно добавить метод Visit(Triangle t) в интерфейс IShapeVisitor, потом реализовать его в каждом посетителе. Если забыли — получите ошибку в рантайме. Или хуже — молчаливое некорректное поведение, если базовый класс имеет реализацию по умолчанию.

В switch выражении компилятор выдаст предупреждение, что Triangle не обработан. Вы узнаете об этом до запуска, на этапе сборки.

// CS8509: The switch expression does not handle all possible values
public static double CalculateArea(IShape shape) => shape switch
{
Circle c => Math.PI * c.Radius * c.Radius,
Rectangle r => r.Width * r.Height
// Triangle не обработан — компилятор предупредит
};


Когда Visitor всё ещё уместен

Если иерархия типов находится в чужой библиотеке и вы не можете на неё влиять, а switch по типам становится слишком длинным и разбросанным по кодовой базе — Visitor помогает собрать логику в одном месте. Также он полезен, когда операция требует сложного состояния между вызовами, которое неудобно протаскивать через отдельные методы.

Но для большинства задач, где вы контролируете код и типы, switch выражения проще, короче и безопаснее. Компилятор работает на вашей стороне, а не против вас.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#il_люминатор
  • ❤ 8
  • 🤔 3
More from @csharpproglib
  1. Sep 21, 2026⚙️ Настоящие атомарные операции Если Volatile решает проблему видимости, то Interlocked ре…
  2. Sep 20, 2026💪 Разминка перед трудовыми буднями Что произойдёт? ❤️ — список станет [1, 3] 🔥 — Invalid…
  3. Sep 19, 2026🧩 Middleware в ASP.NET Core: 3 ловушки Middleware — звено HTTP pipeline: app.Use(async (c…
  4. Sep 18, 2026📍 Навигация: Вакансии • Задачи • Собесы 🐸Библиотека шарписта #garbage_collector
  5. Sep 17, 2026🦆 Duck typing в C# — почти как в TypeScript В TypeScript достаточно, чтобы у объекта был…
  6. Sep 17, 2026🧠 Почему .NET-под съедает всю память? kubectl top pod показывает 1 ГБ, а managed heap зан…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →