$ sudo teach IT
Модуль 8 · Функции

Урок 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, ...) { } }

C# · Простейший extension метод
// 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. Остальные параметры — как у обычного метода.

C# · Правила объявления extension методов
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 методы особенно удобны для создания читаемых цепочек операций. Вот несколько полезных примеров:

C# · Примеры 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.

C# · LINQ — это extension методы
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
    )
);
💡 Именно благодаря extension методам возможен «fluent» стиль LINQ, когда методы выстраиваются в цепочку. Каждый метод возвращает IEnumerable<T>, и следующий метод вызывается на нём.
⚠️

Ограничения и best practices

Extension методы — мощный инструмент, но важно знать их ограничения и использовать с умом.

Ограничения:

  • Не могут переопределять методы класса — если у класса есть метод с той же сигнатурой, extension метод не вызовется.
  • Не имеют доступа к private членам расширяемого класса.
  • Не могут быть виртуальными — не поддерживают полиморфизм.
  • Должны быть в статическом классе — их нельзя объявить внутри обычного класса.
  • Работают только через using — нужно импортировать пространство имён.
  • Могут вызвать путаницу — если переусердствовать, код станет труднее читать.

Best practices:

  • Используй для очевидных операций — не создавай метод, если его назначение неясно из контекста.
  • Названия классов с суффиксом Extensions — StringExtensions, EnumerableExtensions.
  • Не перегружай — не добавляй extension метод, если то же самое можно сделать обычным методом.
  • Документируй — пиши XML-комментарии к extension методам (это особенно важно).
  • Храни все расширения для одного типа в одном классе.
  • Не используй, если нужен доступ к private/protected членам — это невозможно.
❓

Мини-тест

Вопрос 1: Extension метод — это:

A) Метод, добавляемый в класс через наследование
B) Статический метод, вызываемый как экземплярный
C) Динамический метод, добавляемый в рантайме
D) Метод, изменяющий исходный класс

Правильный ответ: B

Вопрос 2: Какой модификатор нужен у первого параметра extension метода?

A) ref
B) out
C) in
D) this

Правильный ответ: D

Вопрос 3: Какой класс должен содержать extension методы?

A) Обычный класс (class)
B) Статический класс (static class)
C) Абстрактный класс
D) Интерфейс

Правильный ответ: B

Вопрос 4: Какой метод является основой LINQ?

A) Обычные статические методы
B) Абстрактные методы
C) Extension методы
D) Анонимные методы

Правильный ответ: C

Вопрос 5: Что будет, если у класса есть метод с той же сигнатурой, что и extension метод?

A) Будет вызван extension метод
B) Будет вызван метод класса (extension игнорируется)
C) Ошибка компиляции
D) Исключение в рантайме

Правильный ответ: B

🧪

Задание

Библиотека полезных расширений

Создай консольное приложение и добавь статический класс StringExtensions со следующими extension методами:

  1. IsPalindrome(this string str) — проверяет, является ли строка палиндромом (без учёта регистра).
  2. CountWords(this string str) — возвращает количество слов в строке.
  3. Truncate(this string str, int maxLength) — обрезает строку до maxLength символов, добавляя "...".
  4. ToTitleCase(this string str) — каждое слово с заглавной буквы.
  5. ExtractDigits(this string str) — возвращает строку, содержащую только цифры.
  6. Проверь все методы в Main() на разных примерах.

Цель

Отработать создание и использование extension методов.

Сложность

Лёгкая

📌

Что важно запомнить

1️⃣
Extension метод — статический метод в статическом классе, первый параметр с this.
2️⃣
Вызывается как экземплярный метод, хотя на самом деле это статический вызов. Синтаксис: "hello".MyMethod().
3️⃣
LINQ целиком построен на extension методах: Where(), Select(), OrderBy() — всё это расширения для IEnumerable<T>.
4️⃣
Не могут переопределять существующие методы класса. Метод класса всегда имеет приоритет.
5️⃣
Используй с умом: называй классы с суффиксом Extensions, импортируй через using, не переусердствуй с количеством.

Тест: 8.10: Extension методы

3 вопроса

Extension методы

Premium