Программирование >>  Программирование с использованием ajax 

1 ... 113 114 115 [ 116 ] 117 118 119 ... 396


farm.MakeNoises ();

Farm<Cow> dairyFarm = farm,GetCows (); dairyFarm. FeedTheAnimals () ;

foreach (Cow cow in dairyFarm) {

if (cow is SuperCow) {

(cow as SuperCow).Fly();

Console.ReadKey();

11. Запустите приложение. На рис. 12.5 показан результат, который должен получиться.


Рис. 12.5. Приложение Chi 2 Ех О 4 в действии

Описание полученных результатов

в этом примере был создан обобщенный класс Farm<T>, который вместо того, чтобы наследоваться от обобщенного класса списка, предоставляет таковой в виде общедоступного свойства. Тип этого списка определяет параметр типа Т, который передается классу Farm<T> и ограничивается только типом, который либо сам является, либо унаследован от Animal:

public class Farm<T> : IEnumerable<T> where T : Animal

private List<T> animals = new List<T>(); public List<T> Animals

get {

return animals;

Еще Farm<T> реализует интерфейс IEnumerable<T>, тоже с передачей ему типа Т, который, следовательно, имеет те же ограничения. Реализуется этот интерфейс для обеспечения возможности итерации по содержащимся в Farm<T> элементами без явного прохода по Farm<T> .Animals. Достигается это очень легко, а именно - возвратом перечислителя, предоставляемого Animals, каковым является класс List<L>, который тоже реализует интерфейс IEnumerable<T>:

public IEnumerator<T> GetEnumerator () {

return animals.GetEnumerator0;



Поскольку IEnumerable<T> наследуется от lEnumerable, также необходимо реализовать и lEnumerable.GetEnumerator():

lEnumerator lEnumerable.GetEnumerator() {

return animals.GetEnumerator0;

В состав Farm<T> входит два метода, которые пользуются методами абстрактного класса Animal:

public void MakeNoisesО {

foreach (Т animal in animals) {

animal.MakeANoise ();

public void FeedTheAnimals0 {

foreach (T animal in animals) {

animal.Feed();

Поскольку T ограничен типом Animal, данный код компилируется нормально - доступ к этим методам гарантирован, каким бы на самом деле не был тип Т.

Следующий метод - GetCows () - более интересен. Он просто извлекает из коллекции все элементы, которые относятся к типу Cow (или наследуются от Cow, как, например, новый класс SuperCow):

public Farm<Cow> GetCows() {

Farm<Cow> cowFarm = new Farm<Cow>();

foreach (T animal in animals)

if (animal is Cow) {

cowFarm.Animals.Add(animal as Cow) ;

return cowFarm;

Интересно здесь то, что данный метод кажется немного неэкономным. При желании иметь другие методы подобного рода, например, GetChickens () и т.п., их тоже нужно было бы реализовать явно. В системе с большим количеством типов потребовалось бы реализовать подобным образом еще больше методов. Поэтому гораздо более эффективным решением здесь было бы применение обобщенного метода, пример реализации которого еще будет демонстрироваться позже в главе.

Клиентский код в Program, cs просто тестирует различные методы Farm и на самом деле не содержит ничего такого, о чем бы еще не рассказывалось, поэтому рассматривать этот код более детально не имеет смысла.

Наследование от обобщенных классов

Класс Farm<T> в предыдущем примере, а также несколько других классов, которые демонстрировались ранее в этой главе, наследовались от обобщенного типа. В слу-



чае класса Farm<T> этим типом был интерфейс IEnumerable<T>. Там ограничение, наложенное на тип Т в Farm<T>, привело к накладыванию соответствующего дополнительного ограничения и на тип Т, используемый в IEnumerable<T>. Подобный подход может быть удобным приемом для установки ограничений на иначе никак неограниченные типы, но, конечно, требует соблюдения кое-каких правил.

Во-первых, нельзя снимать с типов ограничения, накладываемые в том типе, от которого выполняется наследование. Другими словами, тип Т, используемый в том типе, от которого выполняется наследование, должен обязательно ограничиваться как минимум настолько же, насколько и этот тип. Например, следующий код является допустимым:

class SuperFarm<T> : Farm<T> where Т : SuperCow

Он будет работать, потому что Т ограничивается типом Animal в классе Farm<T>, а ограничение этого класса до класса SuperCow означает ограничение Т до подмножества именно таких значений. Следующий код, однако, компилироваться не будет:

class SuperFarm<T> : Farm<T> where Т : struct

Здесь точно понятно, что тип Т, предоставляемый SuperFarm<T>, не может быть преобразован в тип Т, пригодный для использования в Farm<T>, поэтому код и не компилируется.

Эта же проблема присутствует даже в ситуациях, когда ограничение является надмножеством:

class SuperFarm<T> : Farm<T> where Т : class

Даже если типы, вроде Animal, и будут разрешены в SuperFarm<T>, другие типы, удовлетворяющие ограничению class, все равно не будут допустимы в Farm<T>. То есть этот код, опять-таки, компилироваться не будет. Данное правило распространяется на абсолютно все типы ограничений, которые демонстрировались ранее в главе.

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

public class Cards : List<Card>, ICloneable

Этот код будет компилироваться, а вот следующий - нет:

public class Cards : List<T>, ICloneable

Здесь не предоставляется никакой информации для Т, поэтому компиляция невозможна.



1 ... 113 114 115 [ 116 ] 117 118 119 ... 396

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки.
Яндекс.Метрика