| Ограничение обобщения | Описание |
|---|---|
| where T: struct | Параметр типа ‹T› должен иметь в цепочке наследования System.ValueType |
| where T: class | Параметр типа ‹T› |
| where T: new() | Параметр типа ‹T› должен иметь конструктор, заданный по умолчанию. Это полезно тогда, когда обобщенный тип должен создать экземпляр параметра типа, а вы не имеете ясных предположений о формате пользовательских конструкторов. Заметьте, что это ограничение должно быть последним в списке ограничений, если у типа их несколько |
| where T: | Параметр типа ‹T› должен быть производным класса, указанного параметром |
| where T: | Параметр типа ‹T› должен реализовывать интерфейс, указанный параметром |
При наложении ограничений с помощью ключевого слова where список ограничений размещается после имени базового класса обобщенного типа и списка интерфейсов. В качестве конкретных примеров рассмотрите следующие ограничения обобщенного класса MyGenericClass.
// Вложенные элементы должны иметь конструктор,
// заданный по умолчанию.
public class MyGenericClass‹T› where T: new() {…}
// Вложенные элементы должны быть классами, реализующими IDrawable
// и поддерживающими конструктор, заданный по умолчанию.
public class MyGenericClass‹T› where T: class, IDrawable, new() {…}
// MyGenericClass получается из МуВаsе и реализует ISomeInterface,
// а вложенные элементы должны быть структурами.
public class MyGenericClass‹T›: MyBase, ISomeInterface where T: struct {…}
При построении обобщенного типа, в котором указано несколько параметров типа, вы можете указать уникальный набор ограничений для каждого из таких параметров.
// ‹К› должен иметь конструктор, заданный по умолчанию,
// а ‹Т› должен реализовывать открытый интерфейс IComparable.
public class MyGenericClass‹K, T› where K: new() where T: IComparable‹T› {…}
Если вы хотите изменить тип CarCollection‹T› так, чтобы в него можно было поместить только производные от Car, вы можете записать следующее.
public' class CarCollection‹T›: IEnumerable‹T› where T: Car {
public void PrintPetName(int роs) {
// Поскольку теперь все элементы должны быть из семейства Car,
// свойство PetName можно вызывать непосредственно.
Console.WriteLine(arCars[pos].PetName);
}
}
При таких ограничениях на CarCollection‹T› реализация PrintPetName() становится очень простой, поскольку теперь компилятор может предполагать, что ‹Т› является производным от Car. Более того, если указанный пользователем параметр типа не совместим с Car, будет сгенерирована ошибка компиляции.
// Ошибка компиляции!
CarCollection‹int› myInts = new CarCollection‹int›();
Вы должны понимать, что обобщенные методы тоже могут использовать ключевое слово where. Например, если нужно гарантировать, чтобы методу Swap(), созданному в этой главе выше, передавались только типы, производные от System. ValueType, измените свой программный код так.
// Этот метод переставит любые типы, характеризуемые значениями.
static void Swap‹T›(ref Т а, ref T b) where T: struct {
…
}