C#. Ограничения в обобщенных методах и делегатах

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


Содержание


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

1. Применение ограничений в обобщенных методах. Пример

Ограничения могут применяться к отдельным методам классов. В этом случае синтаксис объявления ограничения в методе класса следующий

return_type MethodName<T>(parameters) where T : bounds
{
    // ...
}

здесь

  • return_type — тип, возвращаемый методом. Этот тип также может быть типом T;
  • T — тип, которые получает метод в качестве параметра;
  • MethodName(parameters) — имя метода с перечнем параметров;
  • bounds — ограничения, накладываемые на тип T. Ограничение может быть одним из перечня struct, class, BaseClass, Interface, new().

Пример. В примере объявляется два класса:

  • класс MyClass, в котором объявляется обобщенный метод Print<T>();
  • класс A. Ссылка на этот класс будет передаваться в метод Print<T>().

Фрагмент кода классов и метода следующий

class MyClass
{
  // Обрабатываются только типы-ссылки
  public void Print<T>(T obj) where T : class
  {
    Console.WriteLine("obj: " + obj.ToString());
  }
}

// Некоторый класс
class A
{
  // ...
}

...

Как видно из вышеприведенного кода, в классе MyClass метод Print<T>() получает параметром тип T, на который накладывается ограничение

where T : class

Это означает, что в качестве типа T могут быть использованы только типы ссылки.

В функции main() (или других методах) метод Print<T>() может быть использован следующим образом

...

// 1. Объявить экземпляр класса MyClass
MyClass obj1 = new MyClass();

// 2. Объявить экземпляр класса A
A obj2 = new A(); // obj2 - ссылочного типа

// 3. Вызвать метод Print<T> и передать ему ссылочный тип (obj2)
obj1.Print<A>(obj2); // работает!

...

Если попытаться использовать тип-значение, то возникнет ошибка на этапе компиляции

// Вызвать метод Print<T> и передать ему тип-значение (val)
int val = 38; // val - тип-значение (значимый тип)
obj1.Print<int>(val); // здесь ошибка компиляции

 

2. Применение ограничений в обобщенных делегатах. Пример

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

delegate return_type DelegateName<T>(parameters) where T : bounds;

здесь

  • DelegateName — имя делегата, который возвращает тип return_type и получает параметром список parameters;
  • bounds — перечень ограничений, которые накладываются на тип T.

Пример.

Объявляется делегат Oper3, что получает тип T в качестве параметра типа. Тип T ограничивается структурными типами (struct).
Также объявляется структура Operations, которая содержит три статических метода:

  • Max3() — определяет максимальное значение между тремя числами типа double;
  • Min3() — определяет минимальное значение между тремя числами типа double;
  • Avg3() — определяет среднее значение между тремя числами типа double.

 

// Обобщенный делегат, в котором тип T
// должен быть структурного типа
delegate T Oper3<T>(T a, T b, T c) where T : struct;

// Структура, содержащая статические методы оперирования данными
struct Operation
{
  // Максимальное между тремя числами
  public static double Max3(double a, double b, double c)
  {
    double max = a;
    if (max < b) max = b;
    if (max < c) max = c;
    return max;
  }

  // Среднее между тремя числами
  public static double Avg3(double a, double b, double c)
  {
    if (((a > b) && (a < c)) ||
        ((a > c) && (a < b)))
      return a;
    if (((b > a) && (b < c)) ||
        ((b > c) && (b < a)))
      return b;
    return c;
  }

  // Минимальное между тремя числами
  public static double Min3(double a, double b, double c)
  {
    double min = a;
    if (min > b) min = b;
    if (min > c) min = c;
    return min;
  }
}

В другой функции (например, функции main()) делегат Oper3 может быть использован следующим образом

...

// Объявить ссылку на обобщенный делегат
Oper3<double> op;

// Присвоить ссылке адрес метода Operation.Max()
op = Operation.Max3;

// Вызвать метод Operation.Max() через делегат
double max = op(2.8, 3.5, 1.4);
Console.WriteLine("max = {0}", max); // max = 3.5

// Вызвать метод Avg() через делегат op
op = Operation.Avg3;
double avg = op(2.8, 3.5, 1.4);
Console.WriteLine("avg = {0}", avg); // avg = 2.8

...

 

3. Синтаксис объявления ограничения для нескольких типов T1, T2, …, TN. Объявление ограничения в классе

Обобщенный элемент программы (класс, структура, интерфейс, метод, делегат) может получать в качестве параметров несколько типов T1, T2, …, TN.

Синтаксис объявления класса, в котором несколько параметров типа получают следующие ограничения:

class ClassName<T1, T2, ..., TN>
    where T1 : bounds1
    where T2 : bounds2
    ...
    where TN : boundsN
{
    // ...
}

здесь

  • T1, T2, TN — параметры типов, которыми оперирует класс;
  • bounds1, bounds2, boundsN — соответственно ограничения на типы T1, T2, TN.

 

4. Синтаксис объявления обобщенного интерфейса, который получает параметрами типы T1, T2, …, TN

Синтаксис объявления интерфейса, который использует несколько типов, следующий

interface InterfaceName<T1, T2, ..., TN>
    where T1 : bounds1
    where T2 : bounds2
    ...
    where TN : boundsN
{
    // ...
}

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

 

5. Синтаксис объявления обобщенной структуры, которая содержит ограничения для типов T1, T2, …, TN

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

struct StructName<T1, T2, ..., TN>
    where T1 : bounds1
    where T2 : bounds2
    ...
    where TN : boundsN
{
    // ...
}

 

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

Если в некотором элементе программы (класс, структура, интерфейс) объявляется обобщенный метод, который получает параметрами несколько типов T1, T2, …, TN, то для каждого типа могут накладываться ограничения. В этом случае синтаксис объявления метода следующий:

return_type MethodName<T1, T2, ..., TN>(parameters)
    where T1 : bounds1
    where T2 : bounds2
    ...
    where TN : boundsN
{
    // ...
}

здесь

  • return_type – тип, возвращаемый методом;
  • T1, T2, TN – типы-параметры;
  • bounds1, bounds2, boundsN – соответственно ограничения на типы T1, T2, TN;
  • parameters – параметры, которые получает метод.

Пример. Объявляется класс с именем SomeClass, который содержит обобщенный метод Method<T1, T2, T3>, получающий три параметра типа. Для первого параметра T1 устанавливается ограничение struct. Для второго параметра T2 устанавливается ограничение class. Для параметра T3 устанавливается ограничение базового класса FileStream и ограничения на наличие конструктора без параметров.

// Класс, в котором объявлен метод оперирующий тремя типами
class SomeClass
{
  public void Method<T1, T2, T3>()
    where T1 : struct
    where T2 : class
    where T3 : System.IO.FileStream, new()
  {
    // ...
  }
}

 

7. Синтаксис объявления ограничений на несколько типов в обобщенном делегате

Если нужно чтобы делегат оперировал несколькими типами T1, T2, …, TN, на которые накладываются ограничения, то синтаксис объявления такого делегата следующий

delegate return_type DelegateName<T1, T2, ..., TN>(parameters)
    where T1 : bounds1
    where T2 : bounds2
    ...
    where TN : boundsN;

здесь

  • return_type – тип, возвращаемый делегатом;
  • T1, T2, TN – типы-параметры;
  • bounds1, bounds2, boundsN – соответственно ограничения на типы T1, T2, TN;
  • parameters – параметры, которые получает делегат.

 

8. Пример класса, получающего параметрами типы T1, T2 на которые накладываются ограничения

В примере приведен фрагмент программы, в котором объявляется класс, содержащий обобщенный метод. Этот метод получает параметрами два типа T1, T2. На каждый из типов накладывается ограничение типа struct. Это ограничение обязывает указывать параметрами типа только типы-значения (int, double, Int32, Single, bool, Boolean, …).

using System;

namespace ConsoleApp19
{
  // Применение ограничений в обобщенных методах

  // Класс, содержащий обобщенный метод Print2<T1, T2>,
  // который работает только с типами-значениями

  class MyClass
  {
    // Метод Print2() получает два параметра типа T1, T2.
    // Метод выводит информацию об этих типах
    public void Print2<T1, T2>(T1 obj1, T2 obj2)
      where T1 : struct
      where T2 : struct
    {
      // Получить информацию об экземплярах obj1, obj2
      Type tp1 = obj1.GetType();
      Type tp2 = obj2.GetType();

      Console.WriteLine("obj1.Name = " + tp1.Name);
      Console.WriteLine("obj1.IsClass = {0}", tp1.IsClass);

      Console.WriteLine("obj2.Name = " + tp2.Name);
      Console.WriteLine("obj2.IsClass = {0}", tp2.IsClass);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // Использовать метод Print2<T1, T2>() класса MyClass
      // 1. Создать экземпляр класса MyClass()
      MyClass obj1 = new MyClass();

      // 2. Вызвать метод Print2<T1, T2>) экземпляра obj1
      obj1.Print2<int, double>(28, 3.88);

      Console.WriteLine("Ok!");
      Console.ReadKey();
    }
  }
}

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

obj1.Name = Int32
obj1.IsClass = False
obj2.Name = Double
obj2.IsClass = False
Ok!

Если в строке вызова метода Print2<T1, T2> вместо типа-значения указать ссылочный тип, например,

// 2. Указать ссылочный тип
String s1 = "Hello world!";
System.Text.StringBuilder s2 = new System.Text.StringBuilder();

// Этот код содержит ошибку
obj1.Print2<String, System.Text.StringBuilder>(s1, s2); // ошибка компиляции

то компилятор выдаст ошибку. В этом случае сработают ограничения

where T1 : struct
where T2 : struct

в объявлении метода Print2().

 


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