Menu
Coddy logo textTech

Generics (классы и методы)

Часть раздела Объектно-ориентированное программирование путешествия по C# на Coddy — урок 42 из 70.

Обобщения (Generics) позволяют вам писать классы и методы, которые работают с любым типом данных, сохраняя при этом типобезопасность. Вместо того чтобы писать отдельные классы для IntList, StringList и DoubleList, вы пишете один универсальный класс, который обрабатывает их все.

Обобщенный класс использует параметр типа, обычно называемый T, заключенный в угловые скобки:

public class Box<T>
{
    private T content;
    
    public void Store(T item) => content = item;
    public T Retrieve() => content;
}

// Использование с различными типами
Box<int> intBox = new Box<int>();
intBox.Store(42);

Box<string> stringBox = new Box<string>();
stringBox.Store("Hello");

Когда вы создаете Box<int>, компилятор заменяет каждый T на int. Это обеспечивает проверку типов во время компиляции — вы не сможете случайно сохранить строку в боксе int.

Обобщенные методы работают аналогично, позволяя одному методу работать с различными типами:

public static void Swap<T>(ref T a, ref T b)
{
    T temp = a;
    a = b;
    b = temp;
}

int x = 1, y = 2;
Swap(ref x, ref y);  // x=2, y=1

Компилятор выводит тип из аргументов, поэтому вам не нужно писать Swap<int> явно. Обобщения являются основой таких коллекций, как List<T> и Dictionary<TKey, TValue>, позволяя создавать переиспользуемый и типобезопасный код.

challenge icon

Задание

Легко

Давайте создадим универсальную систему Storage<T>, которая может хранить и управлять элементами любого типа. Вы создадите многоразовый класс-контейнер, демонстрирующий мощь дженериков (generics) — напишите один раз, используйте с целыми числами, строками или любым другим типом!

Вы организуете свой код в двух файлах:

  • Storage.cs: Создайте универсальный класс Storage<T> в пространстве имен DataStorage. Ваш контейнер для хранения должен:
    • Иметь закрытый массив типа T для хранения элементов и свойство Count, отслеживающее количество сохраненных элементов
    • Принимать вместимость (capacity) в конструкторе для инициализации внутреннего массива
    • Иметь метод Add(T item), который добавляет элемент, если есть место
    • Иметь метод Get(int index), который возвращает элемент по указанному индексу
    • Иметь метод GetAll(), который возвращает все сохраненные элементы, объединенные строкой ", "
  • Program.cs: В основном файле продемонстрируйте работу вашего универсального класса с двумя разными типами. Создайте Storage<int> и Storage<string>, добавьте элементы в каждый из них и покажите, что один и тот же класс бесперебойно работает с обоими типами.

Вы получите четыре входных значения:

  • Первое целое число для хранения
  • Второе целое число для хранения
  • Первая строка для хранения
  • Вторая строка для хранения

Создайте хранилище для целых чисел вместимостью 5 и хранилище для строк вместимостью 5. Добавьте два целых числа в целочисленное хранилище, а две строки — в строковое хранилище.

Выведите результат в следующем формате:

Int storage: {GetAll()}
Int count: {Count}
First int: {Get(0)}
String storage: {GetAll()}
String count: {Count}
First string: {Get(0)}

Например, если входные данные — 42, 100, Hello и World, вывод должен быть следующим:

Int storage: 42, 100
Int count: 2
First int: 42
String storage: Hello, World
String count: 2
First string: Hello

Обратите внимание, как вы один раз написали класс Storage<T>, но он идеально работает как с int, так и со string — в этом и заключается магия дженериков! Компилятор обеспечивает безопасность типов, поэтому вы не сможете случайно добавить строку в хранилище целых чисел.

Шпаргалка

Обобщения (Generics) позволяют создавать классы и методы, которые работают с любым типом данных, сохраняя при этом типобезопасность.

Обобщенный класс использует параметр типа (обычно T), заключенный в угловые скобки:

public class Box<T>
{
    private T content;
    
    public void Store(T item) => content = item;
    public T Retrieve() => content;
}

При создании экземпляра укажите тип, который заменит T:

Box<int> intBox = new Box<int>();
intBox.Store(42);

Box<string> stringBox = new Box<string>();
stringBox.Store("Hello");

Обобщенные методы позволяют одному методу работать с различными типами:

public static void Swap<T>(ref T a, ref T b)
{
    T temp = a;
    a = b;
    b = temp;
}

int x = 1, y = 2;
Swap(ref x, ref y);  // Compiler infers type from arguments

Обобщения обеспечивают проверку типов во время компиляции и являются основой таких коллекций, как List<T> и Dictionary<TKey, TValue>.

Попробуйте сами

using System;
using DataStorage;

class Program
{
    public static void Main(string[] args)
    {
        // Чтение входных данных
        int num1 = Convert.ToInt32(Console.ReadLine());
        int num2 = Convert.ToInt32(Console.ReadLine());
        string str1 = Console.ReadLine();
        string str2 = Console.ReadLine();

        // TODO: Создать Storage<int> вместимостью 5
        
        // TODO: Добавить два целых числа в хранилище целых чисел
        
        // TODO: Создать Storage<string> вместимостью 5
        
        // TODO: Добавить две строки в хранилище строк
        
        // TODO: Вывести результат в требуемом формате
        // Int storage: {GetAll()}
        // Int count: {Count}
        // First int: {Get(0)}
        // String storage: {GetAll()}
        // String count: {Count}
        // First string: {Get(0)}
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование