Шаблоны. Звучит, как нечто очень далекое от программирования. Однако шаблоны являются крайне полезным инструментом. Фактически все динамические структуры в C# (динамические массивы, списки, очереди, стеки и пр) используют шаблонные методы. Также можно достаточно часто встретить и альтернативные названия такие как обобщенные типы, обобщения или generics C#.

Что такое шаблоны C#?

Предположим, необходимо разработать класс / метод, который работал бы с разными типами данных. Пока мы еще не знаем шаблонов, решением кажется создания отдельного метода для каждого типа данных. Другим решением может оказаться использование типа Object. Однако и подобный вариант крайне сомнителен: неудобен, небезопасен и нерационален с точки зрения использования памяти. Именно в данном случае следует использовать шаблоны. Синтаксис достаточно прост.

public void DoAny<T>(T val1, T val2, T val3);

В данном случае метод DoAny может обработать любой переданный ему тип данных. Буква T в треугольных скобках говорит о шаблонности метода. Следует заметить, что может быть использован любой другой символ. Однако общепринятым считается именно символ T (template — шаблон). Далее, эта буква будет меняться на тип данных, используемый нами при вызове метода. Использование шаблонных методов так же достаточно просто.

DoAny<int> (1, 2, 3); 

DoAny<char> ('a', 'b', 'c');

В треугольных скобках указывается тип данных, который будет использоваться при выполнении метода. Однако следует заметить, что такая попытка вызова метода

DoAny<string> ("a", 2, 3.14);

приведет к ошибке.

Подпишись на группу Вконтакте и Телеграм-канал. Там еще больше полезного контента для программистов.
А на YouTube-канале ты найдешь обучающие видео по программированию. Подписывайся!

Пример Generics C#

    /// <summary>
    /// Класс для демонстрации шаблонов. 
    /// </summary>
    /// <typeparam name="T"> Тип данных.</typeparam>
    public class TemplateTest<T>
    {
        T[] arr = new T[10];
        int index = 0; 

        /// <summary>
        /// Добавление элемента в массив.
        /// </summary>
        /// <param name="value"> Добавляемый элемент.</param>
        /// <returns> Был ли добавлен элемент.</returns>
        public bool Add(T value)
        {
            if (index >= 10)
            {
                return false;
            }

            arr[index++] = value;
            return true;
        }


        /// <summary>
        /// Получить элемент по индексу.
        /// </summary>
        /// <param name="index"> Индекс.</param>
        /// <returns> Элемент по индексу.</returns>
        public T get(int index)
        {
            if (index < this.index && index >= 0)
            {
                return arr[index];
            }
            else
            {
                return default(T);
            }
        }

        /// <summary>
        /// Получить количество элементов в массиве.
        /// </summary>
        /// <returns> Количество добавленных элементов.</returns>
        public int Count()
        {
            return index;
        }
    }

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

var arrayInt = new TemplateTest<int>();

var arrayString = new TemplateTest<string>();

Однако далеко не всегда нам необходимо поддерживать все типы данных. Для этого достаточного указать ограничение шаблона при помощи ключевого слова where.

public class TemplateTest<T> where T: XXXXX

Здесь XXXXX может принимать одно из значений.

  • Class. Шаблон создается для классов, не объявленных как sealed.
  • struct. Использование шаблонов на основе структур.
  • new(). Параметр <T> должен быть классом, обладающим конструктором по умолчанию.
  • Имя_класса. Шаблон поддерживает использование типов данных, являющихся наследником указанного.
  • Имя_интерфейса. Использование классов, которые реализуют указанный интерфейс.

К примеру, при подобном объявлении класса.

public class TemplateTest<T> where T : IEnumerable

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

Обобщения C# — Заключение

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

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

Также рекомендую прочитать статью Что нового в C# 8.0. А также подписывайтесь на группу ВКонтакте, Telegram и YouTube-канал. Там еще больше полезного и интересного для программистов.