Рейтинговые книги
Читем онлайн Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 136 137 138 139 140 141 142 143 144 ... 407
(частичного) кода демонстрируется работа с ключевым словом where.

Примеры использования ключевого слова where

Начнем с предположения о том, что создан специальный обобщенный класс, и необходимо гарантировать наличие в параметре типа стандартного конструктора. Это может быть полезно, когда специальный обобщенный класс должен создавать экземпляры типа Т, потому что стандартный конструктор является единственным конструктором, потенциально общим для всех типов. Кроме того, подобное ограничение Т позволяет получить проверку на этапе компиляции; если Т — ссылочный тип, то программист будет помнить о повторном определении стандартного конструктора в объявлении класса (как вам уже известно, в случае определения собственного конструктора класса стандартный конструктор из него удаляется).

// Класс MyGenericClass является производным от object, в то время как

// содержащиеся в нем элементы должны иметь стандартный конструктор.

public class MyGenericClass<T> where T : new()

{

  ...

}

Обратите внимание, что конструкция where указывает параметр типа, подлежащий ограничению, за которым следует операция двоеточия. После операции двоеточия перечисляются все возможные ограничения (в данном случае — стандартный конструктор). Вот еще один пример:

// Класс MyGenericClass является производным от object, в то время как

// содержащиеся в нем элементы должны относиться к классу, реализующему

// интерфейс IDrawable, и поддерживать стандартный конструктор.

public class MyGenericClass<T> where T : class, IDrawable, new()

{

  ...

}

Здесь к типу T предъявляются три требования. Во-первых, он должен быть ссылочным типом (не структурой), как помечено лексемой class. Во-вторых, Т должен реализовывать интерфейс IDrawable. В-третьих, тип Т также должен иметь стандартный конструктор. Множество ограничений перечисляются в виде списка с разделителями-запятыми, но имейте в виду, что ограничение new() должно указываться последним! Таким образом, представленный далее код не скомпилируется:

// Ошибка! Ограничение new() должно быть последним в списке!

public class MyGenericClass<T> where T : new(), class, IDrawable

{

  ...

}

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

// Тип <К> должен расширять SomeBaseClass и иметь стандартный конструктор,

// в то время как тип <Т> должен быть структурой и реализовывать

// обобщенный интерфейс IComparable.

public class MyGenericClass<K, T> where K : SomeBaseClass, new()

  where T : struct, IComparable<T>

{

  ...

}

Необходимость построения полного специального обобщенного класса коллекции возникает редко; однако ключевое слово where допускается использовать также в обобщенных методах. Например, если нужно гарантировать, что метод Swap<T>() может работать только со структурами, измените его код следующим образом:

// Этот метод меняет местами любые структуры, но не классы.

static void Swap<T>(ref T a, ref T b) where T : struct

{

  ...

}

Обратите внимание, что если ограничить метод Swap<T>() в подобной манере, то менять местами объекты string (как было показано в коде примера) больше не удастся, т.к. string является ссылочным типом.

Отсутствие ограничений операций

В завершение главы следует упомянуть об еще одном факте, связанном с обобщенными методами и ограничениями. При создании обобщенных методов может оказаться неожиданным получение ошибки на этапе компиляции в случае применения к параметрам типа любых операций C# (+, -, *, == и т.д.). Например, только вообразите, насколько полезным оказался бы класс, способный выполнять сложение, вычитание, умножение и деление с обобщенными типами:

// Ошибка на этапе компиляции! Невозможно

// применять операции к параметрам типа!

public class BasicMath<T>

{

  public T Add(T arg1, T arg2)

  { return arg1 + arg2; }

  public T Subtract(T arg1, T arg2)

  { return arg1 - arg2; }

  public T Multiply(T arg1, T arg2)

  { return arg1 * arg2; }

  public T Divide(T arg1, T arg2)

  { return arg1 / arg2; }

}

К сожалению, приведенный выше класс BasicMath<T> не скомпилируется. Хотя это может показаться крупным недостатком, следует вспомнить, что обобщения имеют общий характер. Конечно, числовые данные прекрасно работают с двоичными операциями С#. Тем не менее, справедливости ради, если аргумент <Т> является специальным классом или структурой, то компилятор мог бы предположить, что он поддерживает операции +, -, * и /. В идеале язык C# позволял бы ограничивать обобщенный тип поддерживаемыми операциями, как показано ниже:

// Только в целях иллюстрации!

public class BasicMath<T> where T : operator +, operator -,

  operator *, operator /

{

  public T Add(T arg1, T arg2)

  { return arg1 + arg2; }

  public T Subtract(T arg1, T arg2)

  { return arg1 - arg2; }

  public T Multiply(T arg1, T arg2)

  { return arg1 * arg2; }

  public T Divide(T arg1, T arg2)

  { return arg1 / arg2; }

}

Увы, ограничения операций в текущей версии C# не поддерживаются. Однако достичь желаемого результата можно (хотя и с дополнительными усилиями) путем определения интерфейса, который поддерживает такие операции (интерфейсы C# могут определять операции!), и указания ограничения интерфейса для обобщенного класса. В любом случае первоначальный обзор построения специальных обобщенных типов завершен. Во время исследования типа делегата в главе 12 мы вновь обратимся к теме обобщений.

Резюме

Глава начиналась с рассмотрения необобщенных типов коллекций в пространствах имен System.Collections и System.Collections.Specialized, включая разнообразные проблемы, которые связаны со многими необобщенными контейнерами, в том числе отсутствие безопасности в отношении типов и накладные расходы времени выполнения в форме операций упаковки и распаковки. Как упоминалось, именно по этим причинам в современных приложениях .NET будут использоваться классы обобщенных коллекций из пространств имен System.Collections.Generic и System.Collections.ObjectModel.

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

1 ... 136 137 138 139 140 141 142 143 144 ... 407
На этой странице вы можете бесплатно читать книгу Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен бесплатно.

Оставить комментарий