Ответ
Interceptor (Перехватчик) — это паттерн проектирования, который позволяет перехватывать вызовы методов или обработку запросов для добавления сквозной функциональности (cross-cutting concerns) без модификации основного кода бизнес-логики.
Основные цели:
- Логирование вызовов методов и их параметров.
- Кэширование результатов дорогостоящих операций.
- Валидация и авторизация входящих данных.
- Управление транзакциями (открытие/закрытие, откат).
- Измерение производительности (профилирование).
Реализация в C#: Чаще всего реализуется через:
- Аспектно-ориентированное программирование (AOP) с использованием библиотек (Castle DynamicProxy, PostSharp).
- Паттерн Декоратор для оборачивания сервисов.
- Фильтры в ASP.NET Core (Action Filters, Resource Filters).
Практический пример с Castle.DynamicProxy:
using Castle.DynamicProxy;
// 1. Определяем перехватчик
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine($"[LOG] Вызов метода: {invocation.Method.Name}");
Console.WriteLine($"[LOG] Аргументы: {string.Join(", ", invocation.Arguments)}");
var watch = Stopwatch.StartNew();
invocation.Proceed(); // Продолжаем выполнение оригинального метода
watch.Stop();
Console.WriteLine($"[LOG] Метод выполнен за {watch.ElapsedMilliseconds} мс");
Console.WriteLine($"[LOG] Результат: {invocation.ReturnValue}");
}
}
// 2. Создаем прокси-объект с перехватчиком
public interface IService { void ProcessData(string data); }
public class MyService : IService
{
public virtual void ProcessData(string data) { /* Логика */ }
}
var generator = new ProxyGenerator();
IService proxy = generator.CreateClassProxy<MyService>(new LoggingInterceptor());
// 3. Вызов будет перехвачен
proxy.ProcessData("test");
Ключевые нюансы:
- Для перехвата методов классов (не интерфейсов) методы должны быть помечены как
virtual. - Создание прокси и перехват вносят небольшие накладные расходы на производительность.
- Важно избегать побочных эффектов в перехватчиках, которые могут нарушить основную логику.
Ответ 18+ 🔞
Смотри, есть такая штука — Interceptor, или по-нашему "перехватчик". Это как подслушивающее устройство для твоего кода, только легальное. Суть в чём: ты можешь вклиниться в вызов любого метода и сделать что-то своё — залогировать, закэшировать, проверить права — и всё это без правок в самой бизнес-логике. Красота же, да?
Зачем это вообще нужно, спросишь? Да обычная история:
- Логирование — чтобы потом, когда всё ебнулось, не гадать, что за аргументы пришли и что вернулось.
- Кэширование — если метод жрёт ресурсы как не в себя, можно результат припрятать.
- Валидация и авторизация — отсеять хуйню и непрошенных гостей до того, как они доберутся до ядра.
- Транзакции — автоматически открывать и закрывать эту канитель.
- Замер производительности — понять, какой метод тормозит как черепаха в патруле.
Как это делается в C#? Есть несколько путей, но самые популярные:
- Аспекты (AOP) — через библиотеки вроде Castle DynamicProxy или PostSharp. Это самый мощный и правильный способ, но надо подключать зависимости.
- Декоратор — старый добрый паттерн, когда оборачиваешь сервис в другой класс. Работает, но плодит кучу обёрток, если функционала много.
- Фильтры в ASP.NET Core — если речь про веб, то это родной и удобный инструмент (Action Filters, Resource Filters).
Давай на живом примере через Castle.DynamicProxy, чтобы понятно стало:
using Castle.DynamicProxy;
// 1. Пишем самого перехватчика. Он как шпион в штатском.
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
// Ловим вызов на входе
Console.WriteLine($"[LOG] Вызов метода: {invocation.Method.Name}");
Console.WriteLine($"[LOG] Аргументы: {string.Join(", ", invocation.Arguments)}");
// Засекаем время, вдруг медленно работает
var watch = Stopwatch.StartNew();
// Ключевой момент! Пускаем вызов дальше, к реальному методу.
invocation.Proceed();
watch.Stop();
// Смотрим, что получилось на выходе
Console.WriteLine($"[LOG] Метод выполнен за {watch.ElapsedMilliseconds} мс");
Console.WriteLine($"[LOG] Результат: {invocation.ReturnValue}");
}
}
// 2. Готовим сервис, который будем перехватывать.
public interface IService { void ProcessData(string data); }
// ВАЖНО! Методы в классе должны быть virtual, иначе DynamicProxy не подцепится.
public class MyService : IService
{
public virtual void ProcessData(string data)
{
/* Тут твоя святая бизнес-логика */
}
}
// 3. Магия создания прокси.
var generator = new ProxyGenerator();
// Подсовываем перехватчик нашему сервису
IService proxy = generator.CreateClassProxy<MyService>(new LoggingInterceptor());
// 4. Вызываем. И вуаля — наш шпион уже всё видит и пишет в консоль.
proxy.ProcessData("test");
На что обратить внимание, а то обожжёшься:
- Методы должны быть
virtual. Это обязательное условие для перехвата классов (для интерфейсов — нет). Если забудешь, перехватчик просто проигнорирует вызов, и будешь потом чесать репу. - Производительность. Создание прокси и сам перехват — это дополнительные телодвижения. В 99% случаев пофигу, но если у тебя там ядрёная высоконагруженная система, надо мерить.
- Побочные эффекты. В перехватчике нельзя делать дичь, которая сломает основную логику. Типа, не вздумай глобально менять аргументы или возвращаемое значение без серьёзной причины, а то потом отладка превратится в ад.