C#. Обобщенные интерфейсы

Обобщенные интерфейсы. Синтаксис объявления. Реализация обобщенных интерфейсов в классах. Примеры

Перед изучением данной темы рекомендуется ознакомиться со следующей темой:


Содержание


Поиск на других ресурсах:




1. Синтаксис объявления обобщенного интерфейса

В языке C# допускается объявлять обобщенные интерфейсы. При объявлении обобщенного интерфейса сигнатура методов, которые объявлены в интерфейсе, может содержать ссылки на параметризованные типы. Перечень параметризованных типов (например T1, T2, …, TN) задается в заголовке объявления интерфейса между символами <>.

Общая форма объявления обобщенного интерфейса следующая:

interface InterfaceName<T1, T2, ..., TN>
{
  // ...
}

здесь

  • InterfaceName – имя интерфейса;
  • T1, T2, TN – имена типов, которые выступают в качестве параметров интерфейса.

Класс, реализующий интерфейс InterfaceName<T1, T2, …, TN> должен объявляться следующим образом:

class ClassName<T1, T2, ..., TN> : InterfaceName<T1, T2, ..., TN>
{
  // ...
}

здесь

  • ClassName – имя класса, реализующего интерфейс InterfaceName.

В простейшем случае интерфейс получает некоторый тип T. В этом случае общая форма объявления интерфейса и его реализации в классе имеет вид

interface InterfaceName<T>
{
  // ...
}

class ClassName<T> : InterfaceName<T>
{
  // ...
}

 

2. Примеры реализации обобщенного интерфейса для одиночного типа T
2.1. Реализация основных операций над числами: сложение, вычитание

В примере объявляется обобщенный интерфейс, который получает параметром тип T. В интерфейсе реализованы операции сложения и вычитания чисел.

using System;

namespace ConsoleApp19
{
  // Интерфейс, получающий параметром тип T
  interface Operations<T>
  {
    // Объявление методов, которые используют тип T
    double Add(T var1, T var2);
    double Sub(T var1, T var2);
  }

  // Класс, реализующий интерфейс MyInterface<T1, T2>
  class MyClass<T> : Operations<T>
  {
    // Реализация метода, который использует тип T
    public double Add(T var1, T var2)
    {
      double res = Convert.ToDouble(var1) + Convert.ToDouble(var2);
      return res;
    }

    public double Sub(T var1, T var2)
    {
      double res = Convert.ToDouble(var1) + Convert.ToDouble(var2);
      return res;
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // Демонстрация использования обобщенного интерфейса
      // 1. Для типа-заполнителя int
      MyClass<int> mc1 = new MyClass<int>();
      double res1 = mc1.Add(23, 48);
      Console.WriteLine("res1 = {0}", res1);

      // 2. Для типа-заполнителя float
      MyClass<float> mc2 = new MyClass<float>();
      double res2 = mc2.Sub(8.804f, 1.704f);
      Console.WriteLine("res2 = {0:f}", res2);

      Console.ReadKey();
    }
  }
}

Результат выполнения программы

res1 = 71
res2 = 10.51

 

2.2. Реализация методов обработки массива данных

Объявить обобщенный интерфейс IItems<T>, содержащий следующие методы обработки массива данных типа T:

  • Count() — возвращает количество элементов в массиве;
  • GetValue() — получить элемент по его индексу;
  • SetValue() — установить новый элемент в указанном индексе;
  • AddValue() — добавить элемент в конец массива;
  • Delete() — удалить элемент из заданной позиции.

Объявить обобщенный класс ArrayItems<T>, который реализует интерфейс IItems<T>. Кроме методов интерфейса в классе дополнительно реализовать:

  • массив данных типа T[];
  • конструктор без параметров;
  • конструктор с параметром, который получает другой массив типа T[];
  • метод Print(), который выводит массив в виде, удобном для просмотра.

Текст программы, решающий данную задачу следующий:

using System;

namespace ConsoleApp19
{
  // Обобщенный интерфейс для массива элементов разных типов
  interface IItems<T>
  {
    // Количество элементов в массиве
    int Count();

    // Получить элемент по его индексу
    T GetValue(int index);

    // Установить новый элемент в указанном индексе
    void SetValue(T value, int index);

    // Добавить элемент в конец массива
    void AddValue(T value);

    // Удалить элемент с позиции index
    void Delete(int index);
  }

  // Класс, реализующий интерфейс IItems<T>
  class ArrayItems<T> : IItems<T>
  {
    // Внутренний массив
    T[] items;

    // Конструкторы
    public ArrayItems()
    {
      // Установить значение по умолчанию
      items = default(T[]);
    }

    public ArrayItems(T[] _items)
    {
      items = _items;
    }

    // Метод, возвращающий количество элементов в массиве
    public int Count()
    {
      return items.Length;
    }

    // Получить элемент по его индексу
    public T GetValue(int index)
    {
      return items[index];
    }

    // Установить новое значение по индексу
    public void SetValue(T value, int index)
    {
      // Проверка, есть ли индекс в допустимых пределах
      if ((index>=0)&&(index<items.Length))
        items[index] = value;
    }

    // Добавить элемент в конец массива
    public void AddValue(T value)
    {
      // Запомнить ссылку на старый массив
      T[] items2 = items;

      // Создать новый массив
      items = new T[items.Length + 1];

      // items2 => items
      for (int i = 0; i < items.Length - 1; i++)
        items[i] = items2[i];

      // Добавить последний элемент
      items[items.Length - 1] = value;
    }

    // Удалить элемент, находящийся в позиции index
    public void Delete(int index)
    {
      // Проверка index на корректность
      if ((index < 0) || (index >= items.Length))
        return;

      // Создать новый массив с длиной на 1 меньше
      T[] items2 = new T[items.Length - 1];

      // Скопироать элементы обходя позицию index
      for (int i = 0; i < index; i++)
        items2[i] = items[i];
      for (int i = index + 1; i < items.Length; i++)
        items2[i - 1] = items[i];

      // Перенаправить внутреннюю ссылку
      items = items2;
    }

    // Вывод массива на экран
    public void Print(string message)
    {
      Console.Write(message + "\t\t");
      for (int i = 0; i < items.Length; i++)
        Console.Write("{0} ", items[i]);
      Console.WriteLine();
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // Демонстрация работы класса ArrayItems<T> для типа int
      int[] AI = { 2, 8, 3, 4, 11, 17, 5 };
      ArrayItems<int> A1 = new ArrayItems<int>(AI);

      A1.Print("A1:");

      // Добавить элемент
      A1.AddValue(130);
      A1.Print("A1 + [130]:");

      // Удалить другой элемент
      A1.Delete(1);
      A1.Print("A1 - A1[1]:");

      // Заменить третий элемент
      A1.SetValue(111, 2);
      A1.Print("A1[2] = 111: ");

      // Считать элемент по индексу 4
      int item = A1.GetValue(4);
      Console.WriteLine("A1[4] = {0}", item);

      Console.ReadKey();
    }
  }
}

Результат выполнения программы

A1:             2 8 3 4 11 17 5
A1 + [130]:             2 8 3 4 11 17 5 130
A1 - A1[1]:             2 3 4 11 17 5 130
A1[2] = 111:           2 3 111 11 17 5 130
A1[4] = 17

 

3. Пример реализации в классе обобщенного интерфейса для двух типов T1, T2

Объявляется интерфейс MyInterface, который содержит методы вывода данных типов T1, T2. В классе MyClass<T1, T2>, реализующего этот интерфейс, продемонстрировано:

  • реализацию методов интерфейса MyInterface<T1, T2>;
  • доступ к данным типов T1, T2.

 

using System;

namespace ConsoleApp19
{
  // Интерфейс, который получает два параметра типа T1, T2
  interface MyInterface<T1, T2>
  {
    // Объявление методов, использующих типы T1, T2
    void Print1(T1 var1);
    void Print2(T2 var2);
  }

  // Класс, реализующий интерфейс MyInterface<T1, T2>
  class MyClass<T1, T2> : MyInterface<T1, T2>
  {
    // Реализация метода, использующего тип T1
    public void Print1(T1 var1)
    {
      Console.WriteLine("var1 = {0}", var1);
    }

    // Реализация метода, использующего тип T2
    public void Print2(T2 var2)
    {
      Console.WriteLine("var2 = {0}", var2);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // 1. Объявить экземпляр класса с типами-заполнителями int, string
      MyClass<int, string> mc1 = new MyClass<int, string>();
      mc1.Print1(233);
      mc1.Print2("Hello world!");

      // 2. Объявить экземпляр класса с типами-заполнителями double, char
      MyClass<double, char> mc2 = new MyClass<double, char>();
      mc2.Print1(8.77);
      mc2.Print2('z');

      // 3. Реализация для типов long, long
      MyClass<long, long> mc3 = new MyClass<long, long>();
      mc3.Print1(32323L);
      mc3.Print2(30000L);

      Console.ReadKey();
    }
  }
}

Результат выполнения программы

var1 = 233
var2 = Hello world!
var1 = 8.77
var2 = z
var1 = 32323
var2 = 30000

 


Связанные темы