Урок 8.10 — Extension методы
Расширение функциональности существующих типов без наследования.
Представь, что тебе нужно добавить метод к классу string, чтобы проверять, является ли строка email-адресом. Но класс string — встроенный, его нельзя изменить. В C# есть элегантное решение: extension методы (расширения). Ты можешь добавить любой метод к любому типу, включая string, int, List<T> и даже интерфейсы. Именно на них построен LINQ!
Что такое extension методы?
Extension метод (метод расширения) — это статический метод, который вызывается как обычный метод экземпляра. Он объявляется в статическом классе, и первый параметр метода помечается ключевым словом this, указывая, к какому типу «прикрепляется» метод.
Общая схема:
static class MyExtensions { static returnType Method(this TargetType obj, ...) { } }
// 1. Создаём статический класс
public static class StringExtensions
{
// 2. Статический метод с this перед первым параметром
public static bool IsEmail(this string str)
{
return str.Contains("@") && str.Contains(".");
}
}
// Использование:
string email = "user@example.com";
string name = "Анна";
Console.WriteLine(email.IsEmail()); // → True
Console.WriteLine(name.IsEmail()); // → False
// Выглядит так, как будто метод IsEmail есть у класса string!
Синтаксис: static class + static method с this
Extension метод обязан находиться в статическом классе и быть статическим. Первый параметр обязателен — это тип, к которому применяется метод, с префиксом this. Остальные параметры — как у обычного метода.
public static class IntExtensions
{
// Extension метод для int
public static bool IsEven(this int number)
{
return number % 2 == 0;
}
// Extension метод с дополнительными параметрами
public static int Multiply(this int number, int by)
{
return number * by;
}
}
// Использование:
int value = 7;
Console.WriteLine(value.IsEven()); // → False
Console.WriteLine(value.Multiply(3)); // → 21
Console.WriteLine(12.IsEven()); // → True (можно и с литералом!)
Console.WriteLine((5).Multiply(4)); // → 20
Ключевые правила:
- Класс должен быть
static. - Метод должен быть
static. - Первый параметр — тип, к которому применяется метод, с модификатором
this. - Extension метод не может переопределять существующий метод класса.
- Если у класса есть метод с той же сигнатурой — приоритет у метода класса, а не расширения.
- Пространство имён с extension методами нужно импортировать через
using.
Практические примеры
Extension методы особенно удобны для создания читаемых цепочек операций. Вот несколько полезных примеров:
public static class UsefulExtensions
{
// Первая буква строки в верхний регистр
public static string FirstCharToUpper(this string str)
{
if (string.IsNullOrEmpty(str)) return str;
return char.ToUpper(str[0]) + str[1..];
}
// Проверка, что строка является числом
public static bool IsNumeric(this string str)
{
return int.TryParse(str, out _);
}
// Ограничение списка определённым количеством элементов
public static List<T> TakeTop<T>(this List<T> list, int count)
{
return list.Take(count).ToList();
}
// Повтор строки N раз
public static string Repeat(this string str, int times)
{
return string.Concat(Enumerable.Repeat(str, times));
}
// Безопасное получение элемента по индексу
public static T? GetOrDefault<T>(this List<T> list, int index)
{
if (index >= 0 && index < list.Count)
return list[index];
return default;
}
}
// Использование:
Console.WriteLine("привет".FirstCharToUpper()); // → Привет
Console.WriteLine("123".IsNumeric()); // → True
Console.WriteLine("abc".IsNumeric()); // → False
Console.WriteLine("Ha".Repeat(3)); // → HaHaHa
var numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
var top3 = numbers.TakeTop(3); // → [1, 2, 3]
Console.WriteLine(numbers.GetOrDefault(10)); // → 0 (default)
Extension методы в LINQ
Самый известный пример использования extension методов — это LINQ (Language Integrated Query). Все методы LINQ — Where(), Select(), OrderBy(), ToList() и другие — это extension методы, определённые в статическом классе Enumerable в пространстве имён System.Linq.
using System.Linq; // ← именно здесь определены extension методы LINQ
int[] numbers = { 5, 2, 8, 1, 9, 3, 7 };
// Все эти методы — extension:
var result = numbers
.Where(n => n > 4) // фильтр
.OrderBy(n => n) // сортировка
.Select(n => n * 10) // преобразование
.ToList(); // преобразование в список
Console.WriteLine(string.Join(", ", result)); // → 50, 70, 80, 90
// Без extension методов код выглядел бы уродливо:
var result2 = Enumerable.ToList(
Enumerable.Select(
Enumerable.OrderBy(
Enumerable.Where(numbers, n => n > 4),
n => n
),
n => n * 10
)
);
IEnumerable<T>, и следующий метод вызывается на нём.Ограничения и best practices
Extension методы — мощный инструмент, но важно знать их ограничения и использовать с умом.
Ограничения:
- Не могут переопределять методы класса — если у класса есть метод с той же сигнатурой, extension метод не вызовется.
- Не имеют доступа к private членам расширяемого класса.
- Не могут быть виртуальными — не поддерживают полиморфизм.
- Должны быть в статическом классе — их нельзя объявить внутри обычного класса.
- Работают только через using — нужно импортировать пространство имён.
- Могут вызвать путаницу — если переусердствовать, код станет труднее читать.
Best practices:
- Используй для очевидных операций — не создавай метод, если его назначение неясно из контекста.
- Названия классов с суффиксом Extensions —
StringExtensions,EnumerableExtensions. - Не перегружай — не добавляй extension метод, если то же самое можно сделать обычным методом.
- Документируй — пиши XML-комментарии к extension методам (это особенно важно).
- Храни все расширения для одного типа в одном классе.
- Не используй, если нужен доступ к private/protected членам — это невозможно.
Мини-тест
Вопрос 1: Extension метод — это:
Правильный ответ: B
Вопрос 2: Какой модификатор нужен у первого параметра extension метода?
Правильный ответ: D
Вопрос 3: Какой класс должен содержать extension методы?
Правильный ответ: B
Вопрос 4: Какой метод является основой LINQ?
Правильный ответ: C
Вопрос 5: Что будет, если у класса есть метод с той же сигнатурой, что и extension метод?
Правильный ответ: B
Задание
Библиотека полезных расширений
Создай консольное приложение и добавь статический класс StringExtensions со следующими extension методами:
IsPalindrome(this string str)— проверяет, является ли строка палиндромом (без учёта регистра).CountWords(this string str)— возвращает количество слов в строке.Truncate(this string str, int maxLength)— обрезает строку до maxLength символов, добавляя "...".ToTitleCase(this string str)— каждое слово с заглавной буквы.ExtractDigits(this string str)— возвращает строку, содержащую только цифры.- Проверь все методы в Main() на разных примерах.
Цель
Отработать создание и использование extension методов.
Сложность
Лёгкая
Что важно запомнить
this."hello".MyMethod().Where(), Select(), OrderBy() — всё это расширения для IEnumerable<T>.Тест: 8.10: Extension методы
3 вопроса