Полное руководство по языку программирования С# 14 и платформе .NET 10 / Дополнительные возможности ООП в C#

Методы расширений

Начальный ⏱ 7 мин чтения 👁 2 🕓 обновлено 11.08.2026

Расширения типов позволяют добавлять новые компоненты - методы, свойства и операторы к уже существующим типам без создания нового производного типа, перекомпиляции или иного изменения исходного типа. Даже если нет доступа к исходному коду этого типа. Для расширения типов применяются два типа синтаксиса: методы расширений и блоки расширений. Рассмотрим, как их можно использовать.

Методы расширений (extension methods) позволяют добавлять новые методы в уже существующие типы без создания нового производного класса. Эта функциональность бывает особенно полезна, когда нам хочется добавить в некоторый тип новый метод, но сам тип (класс или структуру) мы изменить не можем, поскольку у нас нет доступа к исходному коду. Либо если мы не можем использовать стандартный механизм наследования, например, если классы определенны с модификатором sealed.

Например, нам надо добавить для типа string новый метод:


string s = "Привет мир";
char c = 'и';
int i = s.CharCount(c);
Console.WriteLine(i); 

public static class StringExtension
{
    public static int CharCount(this string str, char c)
    {
        int counter = 0;
        for (int i = 0; i < str.Length; i++)
        {
            if (str[i] == c)
                counter++;
        }
        return counter;
    }
}

Для того, чтобы создать метод расширения, вначале надо создать статический класс, который и будет содержать этот метод. В данном случае это класс StringExtension. Затем объявляем статический метод. Суть нашего метода расширения - подсчет количества определенных символов в строке.

Собственно метод расширения - это обычный статический метод, который в качестве первого параметра всегда принимает такую конструкцию: this имя_типа название_параметра, то есть в нашем случае this string str. Так как наш метод будет относиться к типу string, то мы и используем данный тип.

Затем у всех строк мы можем вызвать данный метод:

int i = s.CharCount(c);

Причем нам уже не надо указывать первый параметр. Значения для остальных параметров передаются в обычном порядке.

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

Также следует учитывать, что методы расширения действуют на уровне пространства имен. То есть, если добавить в проект другое пространство имен, то метод не будет применяться к строкам, и в этом случае надо будет подключить пространство имен метода через директиву using.

Начиная с версии C# 14 можно использовать новый синтаксис для определения новых компонентов уже существующих типов - с помощью блоков extension, которые далее мы будем именовать блоками расширений

Блоки расширения поддерживают определение методов, свойств и операторов. С помощью блоков расширения можно определять два типа расширений:

  • Расширения экземпляров: расширяют экземпляр типа

  • Статические расширения: расширяют сам тип

Синтаксически блоки расширений - это блоки кода, которые предваряются словом extension:


extension (тип получатель | тип){

    // определения
    // методов
    // свойств 
    // операторов
}

После слова extension в круглых скобках указывается тип, для которого определяется расширение. Для расширений экземпляров после типа также указывается получатель - по сути переменная этого типа, через которую внутри блока расширения можно ссылаться на объект типа.

Затем в фигурных скобках располагаются определения методов, свойств и операторов, которыми расширяется указанный тип.

Блоки расширения размещаются в статическом классе, который не является обобщенным и не вложен в другой класс.

Причем, стоит отметить, что extension - это не ключевое слово, а контекстное (контекстуальное) слово, и мы вполне его можем использовать в другом значение, например, в качестве имени переменной:


var extension = "ext";
Console.WriteLine(extension);

Расширения экземпляров

Расширения экземпляров применяются к эземпляру типа. Например, используем блоки расширений для добавления к типу string метода подсчета вхождений определенного символа:


static class StringExtension
{
    extension(string str)
    {
        public int CharCount(char c)
        {
            int counter = 0;
            for (int i = 0; i < str.Length; i++)
            {
                if (str[i] == c) counter++;
            }
            return counter;
        }
    }
}

Здесь блок расширения определен для типа string, а в качестве получателя выступает str. То есть через строку str мы сможем ссылать на строку, к которой применяется расширение. В данном случае внутри блока определен только один метод CharCount, который получает символ и вычисляет количество его вхождений в строке.

Далее мы можем применить этот метод:


string s = "Привет мир";
char c = 'и';
int i = s.CharCount(c);
Console.WriteLine(i);   // 2
 
static class StringExtension
{
    extension(string str)
    {
        public int CharCount(char c)
        {
            int counter = 0;
            for (int i = 0; i < str.Length; i++)
            {
                if (str[i] == c) counter++;
            }
            return counter;
        }
    }
}

Аналогичным образом можно определять свойства. Например, добавим для строки свойство, которое будет показывать, является ли строка целым числом:


string str1 = "Hello world";
Console.WriteLine(str1.IsNumber);   // false

string str2 = "1234567";
Console.WriteLine(str2.IsNumber);   // true

string str3 = "123456yyy678";
Console.WriteLine(str3.IsNumber);   // false
 
static class StringExtension
{
    extension(string str)
    {
        public bool IsNumber
        {
            get
            {
                for (int i = 0; i < str.Length; i++)
                {
                    if (str[i] < '0' || str[i] > '9') return false;
                }
                return true;
            }
        }
    }
}

Здесь для строк добавлено свойство IsNumber, которое просто проходит по всем символам строки и, если символ выходит за границы диапазона [1-9], то возвращает false (то есть строка не является целым числом).

Статические расширения

Статические расширения применяются к типу в целом. Методы и свойства статических расширений определяются с модификатором static. Например:


Console.WriteLine(string.Default);

static class StringExtension
{
    extension(string)   // для статических расширений указываем только тип
    {
        public static string Default => "Hello World";
    }
}

Здесь для типа string определено расширение StringExtension с одним свойством - Default, которое просто возвращает некоторую строку по умолчанию.

Поскольку это расширение статическое, для для обращения к его компонентам применяется имя типа: string.Default

Другой распространенный пример статических расширений - определение операторов. Например, добавим для строк операции сравнения:


Console.WriteLine("hello" > "ello");  // true
Console.WriteLine("hello" < "ello");  // false

static class StringExtension
{
    extension(string)
    {

        public static bool operator >(string str1, string str2) => str1.Length > str2.Length;
        public static bool operator <(string str1, string str2) => str1.Length < str2.Length;

        public static string Default => "Hello World";
    }
}

В данном случае сравниваем строки по их длине.

Другой пример - попробуем сложить два массива:


int[] nums1 = [1, 2, 3, 4];
int[] nums2 = [6, 7, 8];
int[] nums3 = nums1 + nums2;  // ! ошибка

Однако при попытке скомпилировать этот код, компилятор нам выбросит ошибку, так как для массивов типа int[] оператор сложения не определен.

Теперь используем статические расширения, чтобы решить данную задачу:


int[] nums1 = [1, 2, 3, 4];
int[] nums2 = [6, 7, 8];
int[] nums3 = nums1 + nums2;
foreach(int i in nums3)
{
    Console.Write(i);  
    Console.Write(" "); // пробел - разделитель между числами
}
Console.WriteLine();

static class ArrayExtension
{
    extension(int[])
    {   
        public static int[] operator +(int[] numbers, int[] other)
        {
            // возвращаемый массив, который объединяет оба исходных
            int[] new_numbers = new int[numbers.Length + other.Length];
            int j = 0;  // индекс итогового массива

            // добавляем элементы из первого массива
            for(int i = 0; i < numbers.Length; i++, j++)
            {
                new_numbers[j] = numbers[i];
            }

            // добавляем элементы из второго массива
            for(int i = 0; i < other.Length; i++, j++)
            {
                new_numbers[j] = other[i];
            }
            return new_numbers;
        }
    }
}

Поскольку это статическое расширение, то блок расширений определен для типа (без указания получателя):

extension(int[])

В блоке определен только оператор сложения, который в принципе должен быть статическим. Логика оператора проста - создаем новый массив, который объекдиняет оба складываемых массива

Благодаря этому мы сложем объединить два массива в один. Консольный вывод прогарммы:

1 2 3 4 6 7 8 

Причем в одном классе расширений можно объединять статические расширения и расширения экземпляров:


int[] nums1 = [1, 2, 3, 4];
int[] nums2 = [6, 7, 8];
int[] nums3 = nums1 + nums2;

nums3.Print();

static class ArrayExtension
{
    // расширение экземпляра
    extension(int[] numbers)
    {
        public void Print()
        {
            foreach(int i in numbers)
            {
                Console.Write(i);  
                Console.Write(" ");
            }
            Console.WriteLine();
        }
    }
    // статическое расширение
    extension(int[])
    {   
        public static int[] operator +(int[] numbers, int[] other)
        {
            int[] new_numbers = new int[numbers.Length + other.Length];
            int j = 0;
            for(int i = 0; i < numbers.Length; i++, j++)
            {
                new_numbers[j] = numbers[i];
            }
            for(int i = 0; i < other.Length; i++, j++)
            {
                new_numbers[j] = other[i];
            }
            return new_numbers;
        }
    }
}

Проверь себя

📋 Тест по теме «Методы расширений» Тест по теме

Практические задания

Лёгкое Ручная проверка Заполните пропуск в коде (1)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

string s = "Привет мир";
_____ c = 'и';
int i = s.CharCount(c);
Console.WriteLine(i); 

public static class StringExtension
{
    public static int CharCount(this string str, char c)
    {
        int counter = 0;
        for (int i = 0; i < str.Length; i++)
        {
            if (str[i] == c)
                counter++;
        }
        return counter;
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (2)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

int i = s._____(c);
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (3)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

_____ (тип получатель | тип){

    // определения
    // методов
    // свойств 
    // операторов
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (4)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

var extension = "ext";
Console._____(extension);
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (5)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

static class StringExtension
{
    extension(string str)
    {
        public int CharCount(_____ c)
        {
            int counter = 0;
            for (int i = 0; i < str.Length; i++)
            {
                if (str[i] == c) counter++;
            }
            return counter;
        }
    }
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (1)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

string s = "Привет мир";
_____ c = 'и';
int i = s.CharCount(c);
Console.WriteLine(i); 

public static class StringExtension
{
    public static int CharCount(this string str, char c)
    {
        int counter = _____;
        for (int i = 0; i < str.Length; i++)
        {
            if (str[i] == c)
                counter++;
        }
        return counter;
    }
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (2)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

int i = s._____(c);
Войдите, чтобы отправить решение
Среднее Ручная проверка Расставьте строки кода в правильном порядке
+20 XP

Строки кода из примера этого урока перемешаны. Впишите их в правильном порядке, каждую строку — с новой строки.

    // методов
    // операторов
extension (тип получатель | тип){
    // определения
    // свойств
}
Войдите, чтобы отправить решение
Сложное Ручная проверка Объясните и модифицируйте пример из урока «Методы расширений»
+30 XP

Опишите своими словами, что делает приведённый пример кода из урока «Методы расширений», и предложите одно осмысленное изменение (например, другое входное значение или дополнительное условие). Приведите итоговый код.

string s = "Привет мир";
char c = 'и';
int i = s.CharCount(c);
Console.WriteLine(i); 

public static class StringExtension
{
    public static int CharCount(this string str, char c)
    {
        int counter = 0;
        for (int i = 0; i < str.Length; i++)
        {
            if (str[i] == c)
                counter++;
        }
        return counter;
    }
}
Войдите, чтобы отправить решение
Сложное Ручная проверка Найдите потенциальную проблему в коде урока «Методы расширений»
+30 XP

Изучите приведённый фрагмент кода и опишите, при каких входных данных или условиях он может сработать не так, как ожидается (граничный случай, отсутствие проверки и т.п.). Предложите исправленный вариант.

int i = s.CharCount(c);
Войдите, чтобы отправить решение
Мини-проект Ручная проверка Мини-проект по теме «Методы расширений»
+50 XP

Соберите небольшую программу, которая последовательно демонстрирует ключевые идеи урока. Программа должна быть рабочей и содержать минимум два самостоятельно написанных фрагмента, а не просто копию примера из урока.

Войдите, чтобы отправить решение
Домашнее задание Ручная проверка Домашнее задание по теме «Методы расширений»
+25 XP

Напишите собственную небольшую программу, применяющую материал темы «Методы расширений». Используйте не менее одной конструкции, показанной в примерах урока. Прикрепите код решения и краткое описание того, что он делает.

Войдите, чтобы отправить решение

Обсуждение (0)

Войдите, чтобы оставить комментарий.