|
Программирование >> Программирование с использованием ajax
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 Здесь не предоставляется никакой информации для Т, поэтому компиляция невозможна.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |