// Это синтаксически корректно, но выглядит,
// по крайней мере, странно…
CarCollection‹int› myInts = new CarCollection‹int›();
myInts.AddCar(5);
myInts.AddCar(11);
Чтобы проиллюстрировать другую форму типичного непредусмотренного использования объекта, предположим, что вы создали два новых класса – SportsCar (спортивная машина) и MiniVan (минивэн), – которые являются производными от Car.
public class SportsCar: Car {
public SportsCar(string p, int s): base(p, s){}
// Дополнительные методы для SportsCar.
}
public class MiniVan: Car {
public MiniVan(string p, int a): base(p, s) {}
// Дополнительные методы для MiniVan.
}
В соответствии с законами наследования, в коллекцию CarCollection‹T›, созданную с параметром типа Car, можно добавлять и типы MiniVan и SportsCar.
// CarCollection‹Car› может хранить любой тип, производный от Car.
CarCollection‹Car› myCars = new CarCollection‹Car›();
myInts.AddCar(new MiniVan("Family Truckster", 55);
myInts.AddCar(new SportsCar("Crusher", 40));
Это синтаксически корректно, но что делать, если вдруг понадобится добавить в CarCollection‹T› новый открытый метод, например, с именем PrintPetName()? Такая задача кажется простой – достаточно получить доступ к подходящему элементу из List‹T› и вызвать свойство PetName.
// Ошибка!
// System.Объект не имеет свойства о именем PetName.
public void PrintPetName(int pos) {
Console.WriteLine(arCars[pos].PetName);
}
Однако в таком виде программный код скомпилирован не будет, поскольку истинная суть ‹Т› еще не известна, и вы не можете с уверенностью утверждать, что какой-то элемент типа List‹T› будет иметь свойство PetName. Когда параметр типа не имеет никаких ограничений (как в данном случае), обобщенный тип называется
Вы можете попытаться "обмануть" компилятор путем преобразования элемента, возвращенного из Метода индексатора List‹T›, в строго типизованный объект Car, чтобы затем вызвать petName возвращенного объекта.
// Ошибка!
// Нельзя превратить тип 'Т' в 'Car'!
public void PrintPetName(int pos) {
Console.WriteLine(((Car)arCars[pos]).PetName);
}
Но это тоже не компилируется, поскольку компилятор не знает значения параметра типа ‹Т› и не может гарантировать, что преобразование будет законным.
Для решения именно таких проблем обобщения .NET могут опционально определяться с ограничениями, для чего используется ключевое слово where. В .NET 2.0 обобщения могут иметь ограничения, описанные в табл. 10.2.
Таблица 10.2. Возможные ограничения обобщений для параметров типа