1629295403-b876e2087bddebea4bc9666fb2377a02 (846199), страница 66
Текст из файла (страница 66)
ИнтерфейсомI F o r m a t t a b l e похож на интерфейс I D i s p l a y a b l e , который определялся в других программах. Единственным методом интерфейса I F o r m a t t a b l e является метод ToStrirqjФункция O u t p u t F u n c t i o n () выводит объект I F o r m a t t a b l e с использованиестроки, возвращаемой его методом T o S t r i n g ( ) . У переменной I n t 3 2 , которая реалзует этот метод, не возникает никаких проблем. Но самое интересное, что никаких проблем не возникает и при вызове O u t p u t F u n c t i o n ( 2 ) . Поскольку константа 2 имееттип I n t 3 2 , она также реализует интерфейс I F o r m a t t a b l e .
И наконец, фунцияM a i n () вызывает 3. T o S t r i n g () непосредственно. Вывод этой части функцииM a i n () имеет следующий вид:З н а ч е н и е из O u t p u t F u n c t i o n = 1З н а ч е н и е из O u t p u t F u n c t i o n = 2Непосредственный вывод = 3Далее функция M a i n ( ) объявляет массив объектов типа O b j e c t и сохраняет в первомэлементе массива s t r i n g , во в т о р о м — i n t , в т р е т ь е м — экземпляр класса Program и т.д.Все это можно сделать, так как S t r i n g , I n t 3 2 и P r o g r a m порождены от Object.Итакмассив внутри класса P r o g r a m хранит экземпляр P r o g r a m — правда, это интересно?Затем программа проходит по всем элементам массива. Функция Main() может выбрать из него все целые числа, запрашивая каждый объект, ЯВЛЯЕТСЯли он I n t 3 2 с помощью ключевого слова i s .
Вывод этой части программывыглядит следующим образом:ВыбираемЭлементЭлементиз списка1—23—4целыеПрограмма завершается демонстрацией использования того факта, что все подклассыObj e c t — т.е. все классы — реализуют метод T o S t r i n g ( ) . Таким образом, чтобы вывести все члены массива объектов, вам не надо беспокоиться об их типах. ФункцияM a i n () просто вновь проходит по массиву, вызывая метод T o S t r i n g () для каждогоего элемента. В результате программа выводит на экран следующую информацию:Вывод в с е х о б ъ е к т о в и з с п и с к аO b j e c t s [0]- о т о строка>O b j e c t s [ 1 ] - <2>Objects[2]- < T y p e U n i f i c a t i o n Program>O b j e c t s [ 3 ] - <4>Objects[4]- <5.5>Нажмите < E n t e r > д л я з а в е р ш е н и я п р о г р а м м ы .
. .Для класса P r o g r a m реализован тривиальный метод T o S t r i n g ( ) , просто чтобыпоказать, как это работает.Свойство T o S t r i n g ( ) , несомненно, поясняет магию функции C o n s o l e .W r i t e ( ) . Честно говоря, даже не смотря исходный текст, можно поспорить,что W r i t e () принимает аргументы как принадлежащие типу O b j e c t .
Затемона просто вызывает метод T o S t r i n g () для получения выводимой строки,которая подставляется вместо соответствующего элемента форматирования{п} в первой строке.336Часть V. За базовыми классамиУпаковка типов-значенийЧто действительно делает ссылочные типы и типы-значения наподобие i n t , b o o l ,char и любой структуры гражданами С# первого сорта — так это технология, называемая упаковкой (boxing). Во многих ситуациях компилятор временно конвертируетобъекты типов-значений в ссылочные объекты.
Упаковка означает перемещение частиданных типа-значения в объект ссылочного типа в куче. Вот пример, в котором выполняется упаковка:int i = 9 9 9 ;object о = i;int j = ( i n t ) о ;// П р о с т о й i n t ( т и п - з н а ч е н и е )// П о м е щ а е м i в с с ы л о ч н у ю у п а к о в к у// Получаем 99 9 из у п а к о в к иВсе, что было упаковано, рано или поздно потребует распаковки, которая влечет засобой приведение типа.
В демонстрационной программе T y p e U n i f i c a t i o n каждоеприсваивание o b j e c t требовало упаковки, а обратное преобразование переменной o b ject — распаковки.Обе операции требуют определенного времени. Упаковка до 20 раз продолжительнееобычного присваивания, а распаковка — до 4 раз. Кроме того, упаковка требует дополнительной памяти для размещения объекта в куче, так что большое количество упаковокможет снизить производительность вашей программы. Упаковка во многих ситуацияхвыполняется автоматически, включая такие ситуации, как передача аргумента, возвратзначения из функции, присваивание, работа с массивами o b j e c t [ ] , вызовы W r i t e Line () и многое другое.
По возможности избегайте упаковки — например, вызовамиToString () для значений, передаваемых W r i t e L i n e ( ) , избегая работы с массивамиobject и используя новые обобщенные коллекции, рассматривающиеся в главе 15,"Обобщенное программирование".ими классами[лава 14. Интерфейсы и структуры337Глава 15Обобщенное программированиеКоллекционирование: преимущества и проблемыЭкономия времени и кода с помощью обобщенных коллекцийНаписание собственных обобщенных классов, методов и интерфейсов# предоставляет массу специализированных альтернатив массивам, о которыхречь шла в главе 6, "Объединение данных — классы и массивы". В этой главеI будут рассмотрены списки, стеки, очереди и другие " м а с с и в о п о д о б н ы е " классы коллекций, такие как универсальный A r r a y L i s t , который может использоваться длярешения множества программистских задач.
В отличие от массивов, эти коллекциине являются безопасными с точки зрения типов и могут вызвать определенные накладные расходы.Однако можно сохранить массу времени и усилий, если воспользоваться обобщенной вер6сией. Обобщенные классы (generics) — новая возможность С#, появившаяся в версии 2.0.Обобщенные классы представляют собой классы, методы и интерфейсы, в которых поля типов остаются незаполненными. Чтобы понять, о чем идет речь, рассмотрим конкретный пример.
Так, класс L i s t < T > определяет обобщенный список, очень похожий на A r r a y L i s t .Когда вы используете этот список для создания {инстанцирования) собственного списка, например чисел типа i n t , вы заменяете параметр типа Т конкретным типом i n t :List<int> m y L i s t = new L i s t < i n t > () ; // Список ч и с е л т и п а i n tУниверсальность такого списка состоит в том, что вы можете инстанцироватьList<T> для любого единого типа д а н н ы х — s t r i n g , S t u d e n t , B a n k A c c o u n t —и при этом получить такую же безопасность типов, как у массива, причем без лишних затрат.
Это — супермассив.Обобщенные классы в С# могут быть встроенными, такими как L i s t < T > , и пользовательскими, т.е. написанными вами. После чтения этой главы вы научитесь писать собственные обобщенные классы не хуже встроенных.° Здесь следует сделать небольшое пояснение. Дело в том, что это новая для С# возможность,а потому русскоязычная терминология еще не устоялась. В С++, в котором обобщенное программирование было реализовано существенно раньше, обобщенные, или универсальные классы, называются шаблонами.
В С# такие классы именуются generic class, или просто generic. В русскоязычной литературе встречаются такие переводы, как обобщенные классы, универсальные классы и даже термин"дженерик". В данной книге будет использоваться термин "обобщенные классы". — Примеч. ред.Чтобы понять, что такое обобщенные классы и что в них хорошего, давайте начнемрассмотрения обычных классов-коллекций.Массивы обеспечивают быстрый и эффективный доступ к произвольнымментам. Но зачастую массивы не удовлетворяют вашим требованиям из-заих недостатков.Программа должна объявить размер массива при его создании. В отличие от Viual Basic, С# не позволяет изменять размер массива после его определения!делать, если вы не знаете заранее, массив какого размера вам потребуется?Вставка или удаление элемента из середины массива весьма неэффективна, 1должны сдвинуть все элементы, чтобы освободить память.Для решения этих проблем С# предоставляет ряд необобщенных коллекций в качаз)ве альтернатив массивам.
Каждая из коллекций имеет свои сильные и слабые стороны.Необобщенные коллекцииС# предоставляет ряд хорошо спроектированных альтернатив массивам. В табл. lilописаны несколько наиболее полезных необобщенных коллекций. Вне сомнения, полхарактеристикам вы всегда сможете выбрать подходящий класс для решения стояиперед вами задачи (но не спешите — еще немного, и вы познакомитесь с обобщенами классами).340Часть V. За базовыми классаИспользование необобщенных коллекцийКоллекции легче применять, чем массивы. Для этого нужно инстанцировать объект коллекции, добавить в него элементы и итеративно работать сними (для этого лучше всего воспользоваться циклом f o r e a c h ) .
Приведенная дальше демонстрационная программа иллюстрирует эту последовательность действий.// N o n g e n e r i c C o l l e c t i o n sдемонстрация// классов к о л л е к ц и йusing S y s t e m ;using S y s t e m . C o l l e c t i o n s ;namespace N o n g e n e r i c C o l l e c t i o n s(publicclassиспользованияProgram{// Демонстрация A r r a y L i s t , S t a c k , Queuepublic s t a t i c void M a i n ( s t r i n g [ ]args)иHashtable{//ArrayList/// / И н с т а н ц и р о в а н и е A r r a y L i s t (вы м о ж е т е у к а з а т ь// начальный р а з м е р , но можете э т о г о и не д е л а т ь )A r r a y L i s t a L i s t W i t h S p e c i f i e d S i z e = new A r r a y L i s t ( 1 0 0 0 ) ;A r r a y L i s t a L i s t = new A r r a y L i s t ( ) ;// размер no// умолчанию (16)aList.Add("one");// Добавление в конец спискаaList.Add("two");// В списке - " o n e " , " t w o "a L i s t .
A d d ( " t h r e e " ) ; // В списке - " o n e " , " t w o " , " t h r e e "Console.WriteLine("{0}itemsin the ArrayList:",aList.Count);/ / Цикл с и с п о л ь з о в а н и е м f o r e a c hf o r e a c h ( s t r i n g s in aList){// Выводим с т р о к у и ее и н д е к с в A r r a y L i s tConsole.WriteLine(s + " в ( { о } ) " , aList.IndexOf(s));}////Stack//// Инстанцируем с т е кS t a c k s t a c k = new S t a c k ( ) ;// Вносим э л е м е н т ы в с т е к и с н и м а е м с н е г о один// элементstack.Push("one");stack.Push("two");// "two","one"stack.Push("three"); // "three","two","one"Console.WriteLine("{0} элементов в стеке:",stack.Count);foreach( s t r i n g s in stack){Console.WriteLine(s) ;}(ma15.Обобщенноепрограммирование341s t r i n g sval = ( s t r i n g ) s t a c k .